Added support for binary files and -M notation.
This commit is contained in:
parent
6ad6a84346
commit
4965306762
10 changed files with 148 additions and 142 deletions
|
@ -31,7 +31,7 @@ With no FILE, or when FILE is -, read standard input.
|
|||
-T, --show-tabs display TAB characters as ^I
|
||||
-s, --squeeze-blank suppress repeated empty output lines
|
||||
--version output version information and exit
|
||||
-v, --show-nonprinting use ^ and U+ notation, except for LFD and TAB
|
||||
-v, --show-nonprinting use ^ and M- notation, except for LFD and TAB
|
||||
|
||||
Examples:
|
||||
dcat f - g Output f's contents, then standard input, then g's contents.
|
||||
|
@ -94,7 +94,3 @@ if (result.exitCode == exitSuccess) {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Differences from [GNU cat](https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation)
|
||||
- No binary file support.
|
||||
- The [U+](https://en.wikipedia.org/wiki/Unicode) notation is used instead of `M-` for non-printing characters.
|
|
@ -11,19 +11,18 @@ import 'package:indent/indent.dart';
|
|||
const appName = 'dcat';
|
||||
const appVersion = '1.0.0';
|
||||
const helpFlag = 'help';
|
||||
const homePage = 'https://github.com/ethauvin/dcat';
|
||||
const numberFlag = 'number';
|
||||
const numberNonBlank = 'number-nonblank';
|
||||
const numberNonBlankFlag = 'number-nonblank';
|
||||
const showAllFlag = 'show-all';
|
||||
const showEndsFlag = 'show-ends';
|
||||
const showNonPrintingEndsFlag = 'show-nonprinting-ends';
|
||||
const showNonPrintingFlag = 'show-nonprinting';
|
||||
const showNonPrintingTabsFlag = 'show-nonprinting-tabs';
|
||||
const showTabsFlag = 'show-tabs';
|
||||
const squeezeBlank = 'squeeze-blank';
|
||||
const squeezeBlankFlag = 'squeeze-blank';
|
||||
const versionFlag = 'version';
|
||||
|
||||
const _homePage = 'https://github.com/ethauvin/dcat';
|
||||
|
||||
/// Concatenates files specified in [arguments].
|
||||
///
|
||||
/// Usage: `dcat [option] [file]…`
|
||||
|
@ -47,10 +46,10 @@ Future<int> main(List<String> arguments) async {
|
|||
} else {
|
||||
final paths = argResults.rest;
|
||||
var showEnds = argResults[showEndsFlag];
|
||||
var showTabs = argResults[showTabsFlag];
|
||||
var showLineNumbers = argResults[numberFlag];
|
||||
var showNonBlank = argResults[numberNonBlank];
|
||||
final showNonBlank = argResults[numberNonBlankFlag];
|
||||
var showNonPrinting = argResults[showNonPrintingFlag];
|
||||
var showTabs = argResults[showTabsFlag];
|
||||
|
||||
if (argResults[showNonPrintingEndsFlag]) {
|
||||
showNonPrinting = showEnds = true;
|
||||
|
@ -70,12 +69,12 @@ Future<int> main(List<String> arguments) async {
|
|||
|
||||
final result = await cat(paths, stdout,
|
||||
input: stdin,
|
||||
numberNonBlank: showNonBlank,
|
||||
showEnds: showEnds,
|
||||
showLineNumbers: showLineNumbers,
|
||||
numberNonBlank: showNonBlank,
|
||||
showNonPrinting: showNonPrinting,
|
||||
showTabs: showTabs,
|
||||
squeezeBlank: argResults[squeezeBlank],
|
||||
showNonPrinting: showNonPrinting);
|
||||
squeezeBlank: argResults[squeezeBlankFlag]);
|
||||
|
||||
for (final message in result.messages) {
|
||||
await printError(message);
|
||||
|
@ -93,7 +92,7 @@ Future<ArgParser> setupArgsParser() async {
|
|||
|
||||
parser.addFlag(showAllFlag,
|
||||
negatable: false, abbr: 'A', help: 'equivalent to -vET');
|
||||
parser.addFlag(numberNonBlank,
|
||||
parser.addFlag(numberNonBlankFlag,
|
||||
negatable: false,
|
||||
abbr: 'b',
|
||||
help: 'number nonempty output lines, overrides -n');
|
||||
|
@ -109,7 +108,7 @@ Future<ArgParser> setupArgsParser() async {
|
|||
negatable: false, abbr: 't', help: 'equivalent to -vT');
|
||||
parser.addFlag(showTabsFlag,
|
||||
negatable: false, abbr: 'T', help: 'display TAB characters as ^I');
|
||||
parser.addFlag(squeezeBlank,
|
||||
parser.addFlag(squeezeBlankFlag,
|
||||
negatable: false,
|
||||
abbr: 's',
|
||||
help: 'suppress repeated empty output lines');
|
||||
|
@ -119,7 +118,7 @@ Future<ArgParser> setupArgsParser() async {
|
|||
parser.addFlag(showNonPrintingFlag,
|
||||
negatable: false,
|
||||
abbr: 'v',
|
||||
help: 'use ^ and U+ notation, except for LFD and TAB');
|
||||
help: 'use ^ and M- notation, except for LFD and TAB');
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
@ -131,18 +130,18 @@ Future<void> printError(String message) async {
|
|||
|
||||
/// Prints the version info.
|
||||
Future<int> printVersion() async {
|
||||
print('''$appName (Dart cat) $appVersion
|
||||
stdout.writeln('''$appName (Dart cat) $appVersion
|
||||
Copyright (C) 2021 Erik C. Thauvin
|
||||
License 3-Clause BSD: <https://opensource.org/licenses/BSD-3-Clause>
|
||||
|
||||
Written by Erik C. Thauvin <https://erik.thauvin.net/>
|
||||
Source: $_homePage''');
|
||||
Source: $homePage''');
|
||||
return exitSuccess;
|
||||
}
|
||||
|
||||
/// Prints usage with [options].
|
||||
Future<int> usage(String options) async {
|
||||
print('''Usage: $appName [OPTION]... [FILE]...
|
||||
stdout.writeln('''Usage: $appName [OPTION]... [FILE]...
|
||||
Concatenate FILE(s) to standard output.
|
||||
|
||||
With no FILE, or when FILE is -, read standard input.
|
||||
|
@ -152,6 +151,6 @@ Examples:
|
|||
$appName f - g Output f's contents, then standard input, then g's contents.
|
||||
$appName Copy standard input to standard output.
|
||||
|
||||
Source and documentation: <$_homePage>''');
|
||||
Source and documentation: <$homePage>''');
|
||||
return exitSuccess;
|
||||
}
|
||||
|
|
|
@ -56,12 +56,12 @@
|
|||
<span class="name ">cat</span>(<wbr><ol class="parameter-list"><li><span class="parameter" id="cat-param-paths"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/List-class.html">List</a><span class="signature"><<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/String-class.html">String</a></span>></span></span> <span class="parameter-name">paths</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-output"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-io/IOSink-class.html">IOSink</a></span> <span class="parameter-name">output</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-input">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-async/Stream-class.html">Stream</a><span class="signature"><<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/List-class.html">List</a><span class="signature"><<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/int-class.html">int</a></span>></span></span>></span>?</span> <span class="parameter-name">input</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-showEnds"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showEnds</span> = <span class="default-value">false</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-numberNonBlank"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">numberNonBlank</span> = <span class="default-value">false</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-showEnds"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showEnds</span> = <span class="default-value">false</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-showLineNumbers"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showLineNumbers</span> = <span class="default-value">false</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-showNonPrinting"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showNonPrinting</span> = <span class="default-value">false</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-showTabs"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showTabs</span> = <span class="default-value">false</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-squeezeBlank"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">squeezeBlank</span> = <span class="default-value">false</span>, </span></li>
|
||||
<li><span class="parameter" id="cat-param-showNonPrinting"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showNonPrinting</span> = <span class="default-value">false</span>}</span></li>
|
||||
<li><span class="parameter" id="cat-param-squeezeBlank"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">squeezeBlank</span> = <span class="default-value">false</span>}</span></li>
|
||||
</ol>)
|
||||
|
||||
</section>
|
||||
|
@ -81,20 +81,20 @@
|
|||
<h2><span>Implementation</span></h2>
|
||||
<pre class="language-dart"><code class="language-dart">Future<CatResult> cat(List<String> paths, IOSink output,
|
||||
{Stream<List<int>>? input,
|
||||
bool showEnds = false,
|
||||
bool numberNonBlank = false,
|
||||
bool showEnds = false,
|
||||
bool showLineNumbers = false,
|
||||
bool showNonPrinting = false,
|
||||
bool showTabs = false,
|
||||
bool squeezeBlank = false,
|
||||
bool showNonPrinting = false}) async {
|
||||
bool squeezeBlank = false}) async {
|
||||
final result = CatResult();
|
||||
final lastLine = _LastLine();
|
||||
|
||||
if (paths.isEmpty) {
|
||||
if (input != null) {
|
||||
try {
|
||||
await _writeStream(input, lastLine, output, showEnds, showLineNumbers,
|
||||
numberNonBlank, showTabs, squeezeBlank, showNonPrinting);
|
||||
await _writeStream(input, lastLine, output, numberNonBlank, showEnds,
|
||||
showLineNumbers, showNonPrinting, showTabs, squeezeBlank);
|
||||
} catch (e) {
|
||||
result.addMessage(exitFailure, _formatError(e));
|
||||
}
|
||||
|
@ -108,8 +108,8 @@
|
|||
} else {
|
||||
stream = File(path).openRead();
|
||||
}
|
||||
await _writeStream(stream, lastLine, output, showEnds, showLineNumbers,
|
||||
numberNonBlank, showTabs, squeezeBlank, showNonPrinting);
|
||||
await _writeStream(stream, lastLine, output, numberNonBlank, showEnds,
|
||||
showLineNumbers, showNonPrinting, showTabs, squeezeBlank);
|
||||
} catch (e) {
|
||||
result.addMessage(exitFailure, _formatError(e), path: path);
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
|
||||
<dl class="callables">
|
||||
<dt id="cat" class="callable">
|
||||
<span class="name"><a href="../dcat/cat.html">cat</a></span><span class="signature">(<wbr><span class="parameter" id="cat-param-paths"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/List-class.html">List</a><span class="signature"><<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/String-class.html">String</a></span>></span></span> <span class="parameter-name">paths</span>, </span><span class="parameter" id="cat-param-output"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-io/IOSink-class.html">IOSink</a></span> <span class="parameter-name">output</span>, </span><span class="parameter" id="cat-param-input">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-async/Stream-class.html">Stream</a><span class="signature"><<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/List-class.html">List</a><span class="signature"><<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/int-class.html">int</a></span>></span></span>></span>?</span> <span class="parameter-name">input</span>, </span><span class="parameter" id="cat-param-showEnds"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showEnds</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-numberNonBlank"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">numberNonBlank</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-showLineNumbers"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showLineNumbers</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-showTabs"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showTabs</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-squeezeBlank"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">squeezeBlank</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-showNonPrinting"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showNonPrinting</span> = <span class="default-value">false</span>}</span>)
|
||||
<span class="name"><a href="../dcat/cat.html">cat</a></span><span class="signature">(<wbr><span class="parameter" id="cat-param-paths"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/List-class.html">List</a><span class="signature"><<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/String-class.html">String</a></span>></span></span> <span class="parameter-name">paths</span>, </span><span class="parameter" id="cat-param-output"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-io/IOSink-class.html">IOSink</a></span> <span class="parameter-name">output</span>, </span><span class="parameter" id="cat-param-input">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-async/Stream-class.html">Stream</a><span class="signature"><<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/List-class.html">List</a><span class="signature"><<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/int-class.html">int</a></span>></span></span>></span>?</span> <span class="parameter-name">input</span>, </span><span class="parameter" id="cat-param-numberNonBlank"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">numberNonBlank</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-showEnds"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showEnds</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-showLineNumbers"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showLineNumbers</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-showNonPrinting"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showNonPrinting</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-showTabs"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showTabs</span> = <span class="default-value">false</span>, </span><span class="parameter" id="cat-param-squeezeBlank"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.4/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">squeezeBlank</span> = <span class="default-value">false</span>}</span>)
|
||||
<span class="returntype parameter">→ <a href="https://api.dart.dev/stable/2.14.4/dart-async/Future-class.html">Future</a><span class="signature"><<wbr><span class="type-parameter"><a href="../dcat/CatResult-class.html">CatResult</a></span>></span></span>
|
||||
</span>
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ With no FILE, or when FILE is -, read standard input.
|
|||
-T, --show-tabs display TAB characters as ^I
|
||||
-s, --squeeze-blank suppress repeated empty output lines
|
||||
--version output version information and exit
|
||||
-v, --show-nonprinting use ^ and U+ notation, except for LFD and TAB
|
||||
-v, --show-nonprinting use ^ and M- notation, except for LFD and TAB
|
||||
|
||||
Examples:
|
||||
dcat f - g Output f's contents, then standard input, then g's contents.
|
||||
|
@ -111,11 +111,6 @@ if (result.exitCode == exitSuccess) {
|
|||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h2 id="differences-from-gnu-cathttpswwwgnuorgsoftwarecoreutilsmanualhtml_nodecat-invocationhtmlcat-invocation">Differences from <a href="https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation">GNU cat</a></h2>
|
||||
<ul>
|
||||
<li>No binary file support.</li>
|
||||
<li>The <a href="https://en.wikipedia.org/wiki/Unicode">U+</a> notation is used instead of <code>M-</code> for non-printing characters.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
/// A library to concatenate files to standard output or file.
|
||||
library dcat;
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
/// Failure exit code.
|
||||
|
@ -57,20 +56,20 @@ class _LastLine {
|
|||
/// The remaining optional parameters are similar to the [GNU cat utility](https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation).
|
||||
Future<CatResult> cat(List<String> paths, IOSink output,
|
||||
{Stream<List<int>>? input,
|
||||
bool showEnds = false,
|
||||
bool numberNonBlank = false,
|
||||
bool showEnds = false,
|
||||
bool showLineNumbers = false,
|
||||
bool showNonPrinting = false,
|
||||
bool showTabs = false,
|
||||
bool squeezeBlank = false,
|
||||
bool showNonPrinting = false}) async {
|
||||
bool squeezeBlank = false}) async {
|
||||
final result = CatResult();
|
||||
final lastLine = _LastLine();
|
||||
|
||||
if (paths.isEmpty) {
|
||||
if (input != null) {
|
||||
try {
|
||||
await _writeStream(input, lastLine, output, showEnds, showLineNumbers,
|
||||
numberNonBlank, showTabs, squeezeBlank, showNonPrinting);
|
||||
await _writeStream(input, lastLine, output, numberNonBlank, showEnds,
|
||||
showLineNumbers, showNonPrinting, showTabs, squeezeBlank);
|
||||
} catch (e) {
|
||||
result.addMessage(exitFailure, _formatError(e));
|
||||
}
|
||||
|
@ -84,8 +83,8 @@ Future<CatResult> cat(List<String> paths, IOSink output,
|
|||
} else {
|
||||
stream = File(path).openRead();
|
||||
}
|
||||
await _writeStream(stream, lastLine, output, showEnds, showLineNumbers,
|
||||
numberNonBlank, showTabs, squeezeBlank, showNonPrinting);
|
||||
await _writeStream(stream, lastLine, output, numberNonBlank, showEnds,
|
||||
showLineNumbers, showNonPrinting, showTabs, squeezeBlank);
|
||||
} catch (e) {
|
||||
result.addMessage(exitFailure, _formatError(e), path: path);
|
||||
}
|
||||
|
@ -104,8 +103,6 @@ String _formatError(Object e) {
|
|||
} else {
|
||||
message = e.message;
|
||||
}
|
||||
} else if (e is FormatException) {
|
||||
message = 'Binary file not supported.';
|
||||
} else {
|
||||
message = '$e';
|
||||
}
|
||||
|
@ -114,15 +111,15 @@ String _formatError(Object e) {
|
|||
|
||||
// Writes parsed data from a stream
|
||||
Future<void> _writeStream(
|
||||
Stream stream,
|
||||
Stream<List<int>> stream,
|
||||
_LastLine lastLine,
|
||||
IOSink out,
|
||||
bool numberNonBlank,
|
||||
bool showEnds,
|
||||
bool showLineNumbers,
|
||||
bool numberNonBlank,
|
||||
bool showNonPrinting,
|
||||
bool showTabs,
|
||||
bool squeezeBlank,
|
||||
bool showNonPrinting) async {
|
||||
bool squeezeBlank) async {
|
||||
// No flags
|
||||
if (!showEnds &&
|
||||
!showLineNumbers &&
|
||||
|
@ -130,15 +127,17 @@ Future<void> _writeStream(
|
|||
!showTabs &&
|
||||
!squeezeBlank &&
|
||||
!showNonPrinting) {
|
||||
await stream.transform(utf8.decoder).forEach(out.write);
|
||||
await stream.forEach(out.add);
|
||||
} else {
|
||||
const caret = 94;
|
||||
const questionMark = 63;
|
||||
const tab = 9;
|
||||
int squeeze = 0;
|
||||
final sb = StringBuffer();
|
||||
final List<int> buff = [];
|
||||
|
||||
await stream.forEach((data) {
|
||||
sb.clear();
|
||||
for (final ch in utf8.decode(data).runes) {
|
||||
buff.clear();
|
||||
for (final ch in data) {
|
||||
if (lastLine.lastChar == _lineFeed) {
|
||||
if (squeezeBlank) {
|
||||
if (ch == _lineFeed) {
|
||||
|
@ -153,52 +152,67 @@ Future<void> _writeStream(
|
|||
}
|
||||
if (showLineNumbers || numberNonBlank) {
|
||||
if (!numberNonBlank || ch != _lineFeed) {
|
||||
sb
|
||||
..write('${++lastLine.lineNumber}'.padLeft(6))
|
||||
..write('\t');
|
||||
buff.addAll('${++lastLine.lineNumber}'.padLeft(6).codeUnits);
|
||||
buff.add(tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastLine.lastChar = ch;
|
||||
if (ch == _lineFeed) {
|
||||
if (showEnds) {
|
||||
sb.write('\$');
|
||||
// $ at EOL
|
||||
buff.add(36);
|
||||
}
|
||||
} else if (ch == tab) {
|
||||
if (showTabs) {
|
||||
// TAB
|
||||
sb.write('^I');
|
||||
// TAB (^I)
|
||||
buff
|
||||
..add(caret)
|
||||
..add(73);
|
||||
continue;
|
||||
}
|
||||
} else if (showNonPrinting) {
|
||||
if (ch >= 32) {
|
||||
if (ch < 127) {
|
||||
// ASCII
|
||||
sb.writeCharCode(ch);
|
||||
buff.add(ch);
|
||||
continue;
|
||||
} else if (ch == 127) {
|
||||
// NULL
|
||||
sb.write('^?');
|
||||
// NULL (^?)
|
||||
buff
|
||||
..add(caret)
|
||||
..add(questionMark);
|
||||
continue;
|
||||
} else {
|
||||
// UNICODE
|
||||
sb
|
||||
..write('U+')
|
||||
..write(ch.toRadixString(16).padLeft(4, '0').toUpperCase());
|
||||
// HIGH BIT (M-)
|
||||
buff.add(77);
|
||||
buff.add(45);
|
||||
if (ch >= 128 + 32) {
|
||||
if (ch < 128 + 127) {
|
||||
buff.add(ch - 128);
|
||||
} else {
|
||||
buff
|
||||
..add(caret)
|
||||
..add(questionMark);
|
||||
}
|
||||
} else {
|
||||
buff.add(caret);
|
||||
buff.add(ch - 128 + 64);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// CTRL
|
||||
sb
|
||||
..write('^')
|
||||
..writeCharCode(ch + 64);
|
||||
buff
|
||||
..add(caret)
|
||||
..add(ch + 64);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
sb.writeCharCode(ch);
|
||||
buff.add(ch);
|
||||
}
|
||||
if (sb.isNotEmpty) {
|
||||
out.write(sb);
|
||||
if (buff.isNotEmpty) {
|
||||
out.add(buff);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -274,13 +274,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
string_splitter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: string_splitter
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0+1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -12,4 +12,3 @@ dev_dependencies:
|
|||
dependencies:
|
||||
args: ^2.3.0
|
||||
indent: ^2.0.0
|
||||
string_splitter: ^1.0.0+1
|
||||
|
|
|
@ -22,42 +22,37 @@ void main() {
|
|||
yield text.codeUnits;
|
||||
}
|
||||
|
||||
File tmpFile() =>
|
||||
File makeTmpFile() =>
|
||||
File("${tempDir.path}/tmp-${DateTime.now().millisecondsSinceEpoch}.txt");
|
||||
|
||||
tearDownAll(() => tempDir.delete(recursive: true));
|
||||
|
||||
group('app', () {
|
||||
test('Test Help', () async {
|
||||
test('--help', () async {
|
||||
expect(app.main(['-h']), completion(0));
|
||||
expect(app.main(['--help']), completion(0));
|
||||
exitCode = await app.main(['-h']);
|
||||
expect(exitCode, exitSuccess);
|
||||
});
|
||||
|
||||
test('Test --version', () async {
|
||||
test('--version', () async {
|
||||
expect(app.main(['--version']), completion(0));
|
||||
exitCode = await app.main(['--version']);
|
||||
expect(exitCode, exitSuccess);
|
||||
});
|
||||
|
||||
test('Test -a', () async {
|
||||
test('invalid option', () async {
|
||||
expect(app.main(['-a']), completion(1));
|
||||
exitCode = await app.main(['-a']);
|
||||
expect(exitCode, exitFailure);
|
||||
});
|
||||
|
||||
test('Test directory', () async {
|
||||
test('no directories', () async {
|
||||
exitCode = await app.main(['bin']);
|
||||
expect(exitCode, exitFailure);
|
||||
});
|
||||
|
||||
test('Test binary', () async {
|
||||
exitCode = await app.main([sampleBinary]);
|
||||
expect(exitCode, exitFailure);
|
||||
});
|
||||
|
||||
test('Test missing file', () async {
|
||||
test('missing file', () async {
|
||||
exitCode = await app.main(['foo']);
|
||||
expect(exitCode, exitFailure, reason: 'foo not found');
|
||||
exitCode = await app.main([sourceFile, 'foo']);
|
||||
|
@ -66,16 +61,17 @@ void main() {
|
|||
});
|
||||
|
||||
group('lib', () {
|
||||
test('Test CatResult', () async {
|
||||
test('CatResult defaults', () async {
|
||||
final result = CatResult();
|
||||
expect(result.isSuccess, true, reason: 'success by default');
|
||||
result.addMessage(exitFailure, sampleText);
|
||||
expect(result.isFailure, true, reason: 'is failure');
|
||||
expect(result.messages.first, equals(sampleText), reason: 'message is sample');
|
||||
expect(result.messages.first, equals(sampleText),
|
||||
reason: 'message is sample');
|
||||
});
|
||||
|
||||
test('Test cat source', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat source', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sourceFile], tmp.openWrite());
|
||||
final lines = await tmp.readAsLines();
|
||||
expect(lines.isEmpty, false, reason: 'log is empty');
|
||||
|
@ -84,8 +80,8 @@ void main() {
|
|||
expect(lines.last, equals('}'));
|
||||
});
|
||||
|
||||
test('Test cat -n source', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat -n source', () async {
|
||||
final tmp = makeTmpFile();
|
||||
final result =
|
||||
await cat([sourceFile], tmp.openWrite(), showLineNumbers: true);
|
||||
expect(result.exitCode, 0, reason: 'result code is 0');
|
||||
|
@ -98,8 +94,8 @@ void main() {
|
|||
}
|
||||
});
|
||||
|
||||
test('Test cat source test', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat source, test', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sourceFile, sampleFile], tmp.openWrite());
|
||||
final lines = await tmp.readAsLines();
|
||||
expect(lines.length, greaterThan(10), reason: 'more than 10 lines');
|
||||
|
@ -108,8 +104,8 @@ void main() {
|
|||
expect(lines.last, endsWith('✓'), reason: 'end with checkmark');
|
||||
});
|
||||
|
||||
test('Test cat -E', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat -E', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleFile], tmp.openWrite(), showEnds: true);
|
||||
var hasBlank = false;
|
||||
final lines = await tmp.readAsLines();
|
||||
|
@ -123,8 +119,8 @@ void main() {
|
|||
expect(lines.last, endsWith('✓'), reason: 'has unicode');
|
||||
});
|
||||
|
||||
test('Test cat -bE', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat -bE', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleFile], tmp.openWrite(),
|
||||
numberNonBlank: true, showEnds: true);
|
||||
final lines = await tmp.readAsLines();
|
||||
|
@ -137,8 +133,8 @@ void main() {
|
|||
}
|
||||
});
|
||||
|
||||
test('Test cat -T', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat -T', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleFile], tmp.openWrite(), showTabs: true);
|
||||
var hasTab = false;
|
||||
final lines = await tmp.readAsLines();
|
||||
|
@ -151,8 +147,8 @@ void main() {
|
|||
expect(hasTab, true, reason: 'has tab');
|
||||
});
|
||||
|
||||
test('Test cat -s', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat -s', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleFile], tmp.openWrite(), squeezeBlank: true);
|
||||
var hasSqueeze = true;
|
||||
var prevLine = 'foo';
|
||||
|
@ -166,26 +162,26 @@ void main() {
|
|||
expect(hasSqueeze, true, reason: 'has squeeze');
|
||||
});
|
||||
|
||||
test('Test cat -A', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat -A', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleFile], tmp.openWrite(),
|
||||
showNonPrinting: true, showEnds: true, showTabs: true);
|
||||
final lines = await tmp.readAsLines();
|
||||
expect(lines.first, endsWith('\$'), reason: '\$ at end.');
|
||||
expect(lines.last, equals('^I^A^B^C^DU+00A9^?U+0080U+2713'),
|
||||
expect(lines.last, equals('^I^A^B^C^DM-BM-)^?M-BM-^@M-bM-^\\M-^S'),
|
||||
reason: "no last linefeed");
|
||||
});
|
||||
|
||||
test('Test cat -t', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat -t', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleFile], tmp.openWrite(),
|
||||
showNonPrinting: true, showTabs: true);
|
||||
final lines = await tmp.readAsLines();
|
||||
expect(lines.last, equals('^I^A^B^C^DU+00A9^?U+0080U+2713'));
|
||||
expect(lines.last, equals('^I^A^B^C^DM-BM-)^?M-BM-^@M-bM-^\\M-^S'));
|
||||
});
|
||||
|
||||
test('Test cat -Abs', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat -Abs', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleFile], tmp.openWrite(),
|
||||
showNonPrinting: true,
|
||||
showEnds: true,
|
||||
|
@ -202,8 +198,8 @@ void main() {
|
|||
expect(blankLines, 2, reason: 'only 2 blank lines.');
|
||||
});
|
||||
|
||||
test('Test cat -v', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat -v', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleFile], tmp.openWrite(), showNonPrinting: true);
|
||||
var hasTab = false;
|
||||
final lines = await tmp.readAsLines();
|
||||
|
@ -214,12 +210,12 @@ void main() {
|
|||
}
|
||||
}
|
||||
expect(hasTab, true, reason: "has real tab");
|
||||
expect(lines.last, equals('\t^A^B^C^DU+00A9^?U+0080U+2713'),
|
||||
expect(lines.last, equals('\t^A^B^C^DM-BM-)^?M-BM-^@M-bM-^\\M-^S'),
|
||||
reason: 'non-printing');
|
||||
});
|
||||
|
||||
test('Test cat to file', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat > file', () async {
|
||||
final tmp = makeTmpFile();
|
||||
final result = await cat([sampleFile], tmp.openWrite());
|
||||
expect(result.isSuccess, true, reason: 'result code is success');
|
||||
expect(result.messages.length, 0, reason: 'messages is empty');
|
||||
|
@ -231,16 +227,22 @@ void main() {
|
|||
expect(lines.last, endsWith('✓'), reason: 'end with checkmark');
|
||||
});
|
||||
|
||||
test('Test cat with file and binary', () async {
|
||||
final result = await cat([sampleFile, sampleBinary], stdout);
|
||||
expect(result.isFailure, true, reason: 'result code is failure');
|
||||
expect(result.messages.length, 1, reason: 'as one message');
|
||||
expect(result.messages.first, contains('Binary'),
|
||||
reason: 'message contains binary');
|
||||
test('cat -v binary, file', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleBinary, sampleFile], tmp.openWrite(),
|
||||
showNonPrinting: true);
|
||||
var lines = await tmp.readAsLines();
|
||||
expect(lines.first, startsWith('7z'));
|
||||
});
|
||||
|
||||
test('Test empty stdin', () async {
|
||||
final tmp = tmpFile();
|
||||
test('cat binary', () async {
|
||||
final tmp = makeTmpFile();
|
||||
await cat([sampleBinary], tmp.openWrite());
|
||||
expect(tmp.readAsLines(), throwsException);
|
||||
});
|
||||
|
||||
test('empty stdin', () async {
|
||||
final tmp = makeTmpFile();
|
||||
var result = await cat([], tmp.openWrite(), input: Stream.empty());
|
||||
expect(result.exitCode, exitSuccess, reason: 'cat() is successful');
|
||||
expect(result.messages.length, 0, reason: 'cat() has no message');
|
||||
|
@ -250,32 +252,40 @@ void main() {
|
|||
expect(result.messages.length, 0, reason: 'cat(-) no message');
|
||||
});
|
||||
|
||||
test('Test cat -', () async {
|
||||
var tmp = tmpFile();
|
||||
test('cat -', () async {
|
||||
var tmp = makeTmpFile();
|
||||
final result = await cat(['-'], tmp.openWrite(), input: mockStdin());
|
||||
expect(result.exitCode, exitSuccess, reason: 'result code is successful');
|
||||
expect(result.messages.length, 0, reason: 'no message');
|
||||
tmp = tmpFile();
|
||||
tmp = makeTmpFile();
|
||||
expect(await tmp.exists(), false, reason: 'tmp file does not exists');
|
||||
});
|
||||
|
||||
test('Test cat()', () async {
|
||||
var tmp = tmpFile();
|
||||
test('cat()', () async {
|
||||
var tmp = makeTmpFile();
|
||||
await cat([], tmp.openWrite(), input: mockStdin());
|
||||
var lines = await tmp.readAsLines();
|
||||
expect(lines.first, equals(sampleText), reason: 'cat() is sample text');
|
||||
tmp = tmpFile();
|
||||
tmp = makeTmpFile();
|
||||
await cat([], tmp.openWrite(), input: mockStdin(text: "Line 1\nLine 2"));
|
||||
lines = await tmp.readAsLines();
|
||||
expect(lines.length, 2, reason: "two lines");
|
||||
});
|
||||
|
||||
test('Test cat file -', () async {
|
||||
var tmp = tmpFile();
|
||||
test('cat file -', () async {
|
||||
var tmp = makeTmpFile();
|
||||
await cat([sampleFile, '-'], tmp.openWrite(),
|
||||
input: mockStdin(text: '\n$sampleText'));
|
||||
var lines = await tmp.readAsLines();
|
||||
expect(lines.last, equals(sampleText));
|
||||
});
|
||||
|
||||
test('closed stdout', () async {
|
||||
final tmp = makeTmpFile();
|
||||
final stream = tmp.openWrite();
|
||||
stream.close();
|
||||
final result = await cat([sampleFile], stream);
|
||||
expect(result.messages.first, contains("closed"));
|
||||
});
|
||||
});
|
||||
}
|
BIN
test/test.7z
BIN
test/test.7z
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue