Added support for binary files and -M notation.

This commit is contained in:
Erik C. Thauvin 2021-10-17 21:04:08 -07:00
parent 6ad6a84346
commit 4965306762
10 changed files with 148 additions and 142 deletions

View file

@ -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.

View file

@ -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;
}

View file

@ -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">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/String-class.html">String</a></span>&gt;</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">&lt;<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">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/int-class.html">int</a></span>&gt;</span></span>&gt;</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&lt;CatResult&gt; cat(List&lt;String&gt; paths, IOSink output,
{Stream&lt;List&lt;int&gt;&gt;? 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);
}

View file

@ -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">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/String-class.html">String</a></span>&gt;</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">&lt;<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">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/int-class.html">int</a></span>&gt;</span></span>&gt;</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">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/String-class.html">String</a></span>&gt;</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">&lt;<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">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.4/dart-core/int-class.html">int</a></span>&gt;</span></span>&gt;</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">&#8594; <a href="https://api.dart.dev/stable/2.14.4/dart-async/Future-class.html">Future</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="../dcat/CatResult-class.html">CatResult</a></span>&gt;</span></span>
</span>

View file

@ -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>

View file

@ -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);
}
});
}

View file

@ -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:

View file

@ -12,4 +12,3 @@ dev_dependencies:
dependencies:
args: ^2.3.0
indent: ^2.0.0
string_splitter: ^1.0.0+1

View file

@ -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"));
});
});
}

Binary file not shown.