Added show non-printing option.

This commit is contained in:
Erik C. Thauvin 2021-10-10 20:23:09 -07:00
parent 4948aaa970
commit 9397ebaa37
14 changed files with 316 additions and 298 deletions

View file

@ -5,6 +5,10 @@
A **cat** command-line implemenation in [Dart](https://dart.dev/), inspired by the [Write command-line apps sample code](https://dart.dev/tutorials/server/cmdline). A **cat** command-line implemenation in [Dart](https://dart.dev/), inspired by the [Write command-line apps sample code](https://dart.dev/tutorials/server/cmdline).
## Synopsis
**dcat** copies each file, or standard input if none are given, to standard output.
## Command-Line Usage ## Command-Line Usage
```sh ```sh
@ -16,19 +20,23 @@ Concatenate FILE(s) to standard output.
With no FILE, or when FILE is -, read standard input. With no FILE, or when FILE is -, read standard input.
-b, --number-nonblank number nonempty output lines, overrides -n -A, --show-all equivalent to -vET
-E, --show-ends display $ at end of each line -b, --number-nonblank number nonempty output lines, overrides -n
-h, --help display this help and exit -e, --show-nonprinting-ends equivalent to -vE
-n, --number number all output lines -E, --show-ends display $ at end of each line
-T, --show-tabs display TAB characters as ^I -h, --help display this help and exit
-s, --squeeze-blank suppress repeated empty output lines -n, --number number all output lines
--version output version information and exit -t, --show-nonprinting-tabs equivalent to -vT
-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 M- notation, except for LFD and TAB
Examples: Examples:
dcat f - g Output f's contents, then standard input, then g's contents. dcat f - g Output f's contents, then standard input, then g's contents.
dcat Copy standard input to standard output. dcat Copy standard input to standard output.
``` ```
## Compile Application ## Compile Standalone Application
### *nix ### *nix
```sh ```sh
@ -42,4 +50,5 @@ dart compile exe bin/dcat.dart
## Differences from [GNU cat](https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation) ## Differences from [GNU cat](https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation)
- No binary file support. - No binary file support.
- Line numbers are printed as `X:` where `X` is the line number. - A line is considered terminated by either a `CR` (carriage return), a `LF` (line feed), a `CR+LF` sequence (DOS line ending).
- The non-printing `M-^?` notation is always used for unicode characters.

View file

@ -8,33 +8,43 @@ import 'package:args/args.dart';
import 'package:dcat/dcat.dart'; import 'package:dcat/dcat.dart';
import 'package:indent/indent.dart'; import 'package:indent/indent.dart';
const appName = libName; const appName = 'dcat';
const appVersion = '1.0.0'; const appVersion = '1.0.0';
const helpFlag = 'help'; const helpFlag = 'help';
const nonBlankFlag = 'number-nonblank'; const nonBlankFlag = 'number-nonblank';
const numberFlag = 'number'; const numberFlag = 'number';
const showAllFlag = 'show-all';
const showEndsFlag = 'show-ends'; const showEndsFlag = 'show-ends';
const showNonPrintingEndsFlag = 'show-nonprinting-ends';
const showNonPrintingFlag = 'show-nonprinting';
const showNonPrintingTabsFlag = 'show-nonprinting-tabs';
const showTabsFlag = 'show-tabs'; const showTabsFlag = 'show-tabs';
const squeezeBlank = 'squeeze-blank'; const squeezeBlank = 'squeeze-blank';
const versionFlag = 'version'; const versionFlag = 'version';
/// Concatenates files specified in [arguments]. /// Concatenates files specified in [arguments].
/// ///
/// Usage: `dcat [OPTION]... [FILE]...` /// Usage: `dcat [option] [file]`
Future<int> main(List<String> arguments) async { Future<int> main(List<String> arguments) async {
final parser = ArgParser(); final parser = ArgParser();
Future<int> returnCode; Future<int> returnCode;
exitCode = exitSuccess; exitCode = exitSuccess;
parser.addFlag(showAllFlag,
negatable: false, abbr: 'A', help: 'equivalent to -vET');
parser.addFlag(nonBlankFlag, parser.addFlag(nonBlankFlag,
negatable: false, negatable: false,
abbr: 'b', abbr: 'b',
help: 'number nonempty output lines, overrides -n'); help: 'number nonempty output lines, overrides -n');
parser.addFlag(showNonPrintingEndsFlag,
negatable: false, abbr: 'e', help: 'equivalent to -vE');
parser.addFlag(showEndsFlag, parser.addFlag(showEndsFlag,
negatable: false, abbr: 'E', help: 'display \$ at end of each line'); negatable: false, abbr: 'E', help: 'display \$ at end of each line');
parser.addFlag(helpFlag, parser.addFlag(helpFlag,
negatable: false, abbr: 'h', help: 'display this help and exit'); negatable: false, abbr: 'h', help: 'display this help and exit');
parser.addFlag(numberFlag, parser.addFlag(numberFlag,
negatable: false, abbr: 'n', help: 'number all output lines'); negatable: false, abbr: 'n', help: 'number all output lines');
parser.addFlag(showNonPrintingTabsFlag,
negatable: false, abbr: 't', help: 'equivalent to -vT');
parser.addFlag(showTabsFlag, parser.addFlag(showTabsFlag,
negatable: false, abbr: 'T', help: 'display TAB characters as ^I'); negatable: false, abbr: 'T', help: 'display TAB characters as ^I');
parser.addFlag(squeezeBlank, parser.addFlag(squeezeBlank,
@ -43,6 +53,11 @@ Future<int> main(List<String> arguments) async {
help: 'suppress repeated empty output lines'); help: 'suppress repeated empty output lines');
parser.addFlag(versionFlag, parser.addFlag(versionFlag,
negatable: false, help: 'output version information and exit'); negatable: false, help: 'output version information and exit');
parser.addFlag('ignored', negatable: false, hide: true, abbr: 'u');
parser.addFlag(showNonPrintingFlag,
negatable: false,
abbr: 'v',
help: 'use ^ and M- notation, except for LFD and TAB');
final ArgResults argResults; final ArgResults argResults;
try { try {
@ -59,12 +74,30 @@ Future<int> main(List<String> arguments) async {
returnCode = printVersion(); returnCode = printVersion();
} else { } else {
final paths = argResults.rest; final paths = argResults.rest;
var showEnds = argResults[showEndsFlag];
var showTabs = argResults[showTabsFlag];
var showNonPrinting = argResults[showNonPrintingFlag];
if (argResults[showNonPrintingEndsFlag]) {
showNonPrinting = showEnds = true;
}
if (argResults[showNonPrintingTabsFlag]) {
showNonPrinting = showTabs = true;
}
if (argResults[showAllFlag]) {
showNonPrinting = showEnds = showTabs = true;
}
returnCode = cat(paths, returnCode = cat(paths,
showEnds: argResults[showEndsFlag], appName: appName,
showEnds: showEnds,
showLineNumbers: argResults[numberFlag], showLineNumbers: argResults[numberFlag],
numberNonBlank: argResults[nonBlankFlag], numberNonBlank: argResults[nonBlankFlag],
showTabs: argResults[showTabsFlag], showTabs: showTabs,
squeezeBlank: argResults[squeezeBlank]); squeezeBlank: argResults[squeezeBlank],
showNonPrinting: showNonPrinting);
} }
exitCode = await returnCode; exitCode = await returnCode;
@ -76,7 +109,7 @@ Future<int> printVersion() async {
print('''$appName (Dart cat) $appVersion print('''$appName (Dart cat) $appVersion
Copyright (C) 2021 Erik C. Thauvin Copyright (C) 2021 Erik C. Thauvin
License: 3-Clause BSD <https://opensource.org/licenses/BSD-3-Clause> License: 3-Clause BSD <https://opensource.org/licenses/BSD-3-Clause>
Inspired by <https://dart.dev/tutorials/server/cmdline> Inspired by <https://dart.dev/tutorials/server/cmdline>
Written by Erik C. Thauvin <https://erik.thauvin.net/>'''); Written by Erik C. Thauvin <https://erik.thauvin.net/>''');
return exitSuccess; return exitSuccess;
@ -93,7 +126,7 @@ ${options.indent(2)}
Examples: Examples:
$appName f - g Output f's contents, then standard input, then g's contents. $appName f - g Output f's contents, then standard input, then g's contents.
$appName Copy standard input to standard output. $appName Copy standard input to standard output.
Source and documentation: <https://github.com/ethauvin/dcat>'''); Source and documentation: <https://github.com/ethauvin/dcat>''');
return exitSuccess; return exitSuccess;
} }

View file

@ -54,12 +54,14 @@
<span class="returntype"><a href="https://api.dart.dev/stable/2.14.3/dart-async/Future-class.html">Future</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.3/dart-core/int-class.html">int</a></span>&gt;</span></span> <span class="returntype"><a href="https://api.dart.dev/stable/2.14.3/dart-async/Future-class.html">Future</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.3/dart-core/int-class.html">int</a></span>&gt;</span></span>
<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.3/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.3/dart-core/String-class.html">String</a></span>&gt;</span></span> <span class="parameter-name">paths</span>, </span></li> <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.3/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.3/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-log">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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.3/dart-core/String-class.html">String</a></span>&gt;</span>?</span> <span class="parameter-name">log</span>, </span></li> <li><span class="parameter" id="cat-param-appName">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">appName</span> = <span class="default-value">&#39;&#39;</span>, </span></li>
<li><span class="parameter" id="cat-param-log"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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.3/dart-core/String-class.html">String</a></span>&gt;</span>?</span> <span class="parameter-name">log</span>, </span></li>
<li><span class="parameter" id="cat-param-showEnds"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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-showEnds"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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.3/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-numberNonBlank"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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-showLineNumbers"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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-showLineNumbers"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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-showTabs"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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-showTabs"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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.3/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-squeezeBlank"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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.3/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showNonPrinting</span> = <span class="default-value">false</span>}</span></li>
</ol>) </ol>)
</section> </section>
@ -67,7 +69,7 @@
<section class="desc markdown"> <section class="desc markdown">
<p>Concatenates files in <code>paths</code> to <a href="https://api.dart.dev/stable/2.14.3/dart-io/stdout.html">stdout</a></p> <p>Concatenates files in <code>paths</code> to <a href="https://api.dart.dev/stable/2.14.3/dart-io/stdout.html">stdout</a></p>
<p>The parameters are similar to the <a href="https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation">GNU cat utility</a>. <p>The parameters are similar to the <a href="https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation">GNU cat utility</a>.
Specify a <code>log</code> for debugging purpose.</p> Specify a <code>log</code> for debugging or testing purpose.</p>
</section> </section>
@ -75,19 +77,21 @@ Specify a <code>log</code> for debugging purpose.</p>
<section class="summary source-code" id="source"> <section class="summary source-code" id="source">
<h2><span>Implementation</span></h2> <h2><span>Implementation</span></h2>
<pre class="language-dart"><code class="language-dart">Future&lt;int&gt; cat(List&lt;String&gt; paths, <pre class="language-dart"><code class="language-dart">Future&lt;int&gt; cat(List&lt;String&gt; paths,
{List&lt;String&gt;? log, {String appName = &#39;&#39;,
List&lt;String&gt;? log,
bool showEnds = false, bool showEnds = false,
bool numberNonBlank = false, bool numberNonBlank = false,
bool showLineNumbers = false, bool showLineNumbers = false,
bool showTabs = false, bool showTabs = false,
bool squeezeBlank = false}) async { bool squeezeBlank = false,
bool showNonPrinting = false}) async {
var lineNumber = 1; var lineNumber = 1;
var returnCode = 0; var returnCode = 0;
log?.clear(); log?.clear();
if (paths.isEmpty) { if (paths.isEmpty) {
final lines = await _readStdin(); final lines = await _readStdin();
await _writeLines(lines, lineNumber, log, showEnds, showLineNumbers, await _writeLines(lines, lineNumber, log, showEnds, showLineNumbers,
numberNonBlank, showTabs, squeezeBlank); numberNonBlank, showTabs, squeezeBlank, showNonPrinting);
} else { } else {
for (final path in paths) { for (final path in paths) {
try { try {
@ -99,8 +103,16 @@ Specify a <code>log</code> for debugging purpose.</p>
.bind(File(path).openRead()) .bind(File(path).openRead())
.transform(const LineSplitter()); .transform(const LineSplitter());
} }
lineNumber = await _writeLines(lines, lineNumber, log, showEnds, lineNumber = await _writeLines(
showLineNumbers, numberNonBlank, showTabs, squeezeBlank); lines,
lineNumber,
log,
showEnds,
showLineNumbers,
numberNonBlank,
showTabs,
squeezeBlank,
showNonPrinting);
} on FileSystemException catch (e) { } on FileSystemException catch (e) {
final String? osMessage = e.osError?.message; final String? osMessage = e.osError?.message;
final String message; final String message;
@ -109,11 +121,13 @@ Specify a <code>log</code> for debugging purpose.</p>
} else { } else {
message = e.message; message = e.message;
} }
returnCode = await printError(message, path: path); returnCode = await printError(message, appName: appName, path: path);
} on FormatException { } on FormatException {
returnCode = await printError(&#39;Binary file not supported.&#39;, path: path); returnCode = await printError(&#39;Binary file not supported.&#39;,
appName: appName, path: path);
} catch (e) { } catch (e) {
returnCode = await printError(e.toString(), path: path); returnCode =
await printError(e.toString(), appName: appName, path: path);
} }
} }
} }
@ -146,7 +160,6 @@ Specify a <code>log</code> for debugging purpose.</p>
<li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li>
<li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li> <li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li>
<li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li> <li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li>
<li><a href="../dcat/libName-constant.html">libName</a></li>
<li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li>

View file

@ -49,6 +49,9 @@
</h1></div> </h1></div>
<section class="desc markdown">
<p>Library to concatenate file(s) to standard output,</p>
</section>
@ -88,21 +91,6 @@
</div> </div>
</dd> </dd>
<dt id="libName" class="constant">
<span class="name "><a href="../dcat/libName-constant.html">libName</a></span>
<span class="signature">&#8594; const <a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span>
</dt>
<dd>
<div>
<span class="signature"><code>&#39;dcat&#39;</code></span>
</div>
</dd>
</dl> </dl>
</section> </section>
@ -112,7 +100,7 @@
<dl class="callables"> <dl class="callables">
<dt id="cat" class="callable"> <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.3/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.3/dart-core/String-class.html">String</a></span>&gt;</span></span> <span class="parameter-name">paths</span>, </span><span class="parameter" id="cat-param-log">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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.3/dart-core/String-class.html">String</a></span>&gt;</span>?</span> <span class="parameter-name">log</span>, </span><span class="parameter" id="cat-param-showEnds"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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.3/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.3/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.3/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.3/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">squeezeBlank</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.3/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.3/dart-core/String-class.html">String</a></span>&gt;</span></span> <span class="parameter-name">paths</span>, </span><span class="parameter" id="cat-param-appName">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">appName</span> = <span class="default-value">&#39;&#39;</span>, </span><span class="parameter" id="cat-param-log"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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.3/dart-core/String-class.html">String</a></span>&gt;</span>?</span> <span class="parameter-name">log</span>, </span><span class="parameter" id="cat-param-showEnds"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/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.3/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.3/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.3/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.3/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.3/dart-core/bool-class.html">bool</a></span> <span class="parameter-name">showNonPrinting</span> = <span class="default-value">false</span>}</span>)
<span class="returntype parameter">&#8594; <a href="https://api.dart.dev/stable/2.14.3/dart-async/Future-class.html">Future</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.3/dart-core/int-class.html">int</a></span>&gt;</span></span> <span class="returntype parameter">&#8594; <a href="https://api.dart.dev/stable/2.14.3/dart-async/Future-class.html">Future</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.3/dart-core/int-class.html">int</a></span>&gt;</span></span>
</span> </span>
@ -125,7 +113,7 @@
</dd> </dd>
<dt id="printError" class="callable"> <dt id="printError" class="callable">
<span class="name"><a href="../dcat/printError.html">printError</a></span><span class="signature">(<wbr><span class="parameter" id="printError-param-message"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">message</span>, </span><span class="parameter" id="printError-param-appName">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">appName</span> = <span class="default-value">libName</span>, </span><span class="parameter" id="printError-param-path"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">path</span> = <span class="default-value">&#39;&#39;</span>}</span>) <span class="name"><a href="../dcat/printError.html">printError</a></span><span class="signature">(<wbr><span class="parameter" id="printError-param-message"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">message</span>, </span><span class="parameter" id="printError-param-appName">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">appName</span> = <span class="default-value">&#39;&#39;</span>, </span><span class="parameter" id="printError-param-path"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">path</span> = <span class="default-value">&#39;&#39;</span>}</span>)
<span class="returntype parameter">&#8594; <a href="https://api.dart.dev/stable/2.14.3/dart-async/Future-class.html">Future</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.3/dart-core/int-class.html">int</a></span>&gt;</span></span> <span class="returntype parameter">&#8594; <a href="https://api.dart.dev/stable/2.14.3/dart-async/Future-class.html">Future</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.3/dart-core/int-class.html">int</a></span>&gt;</span></span>
</span> </span>
@ -175,7 +163,6 @@
<li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li>
<li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li> <li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li>
<li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li> <li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li>
<li><a href="../dcat/libName-constant.html">libName</a></li>
<li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li>

View file

@ -89,7 +89,6 @@
<li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li>
<li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li> <li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li>
<li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li> <li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li>
<li><a href="../dcat/libName-constant.html">libName</a></li>
<li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li>

View file

@ -89,7 +89,6 @@
<li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li>
<li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li> <li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li>
<li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li> <li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li>
<li><a href="../dcat/libName-constant.html">libName</a></li>
<li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li>

View file

@ -1,129 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
<meta name="description" content="API docs for the libName constant from the dcat library, for the Dart programming language.">
<title>libName constant - dcat library - Dart API</title>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,300;0,400;0,500;0,700;1,400&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="../static-assets/github.css?v1">
<link rel="stylesheet" href="../static-assets/styles.css?v1">
<link rel="icon" href="../static-assets/favicon.png?v1">
</head>
<body data-base-href="../"
data-using-base-href="false">
<div id="overlay-under-drawer"></div>
<header id="title">
<button id="sidenav-left-toggle" type="button">&nbsp;</button>
<ol class="breadcrumbs gt-separated dark hidden-xs">
<li><a href="../index.html">dcat</a></li>
<li><a href="../dcat/dcat-library.html">dcat</a></li>
<li class="self-crumb">libName constant</li>
</ol>
<div class="self-name">libName</div>
<form class="search navbar-right" role="search">
<input type="text" id="search-box" autocomplete="off" disabled class="form-control typeahead" placeholder="Loading search...">
</form>
</header>
<main>
<div id="dartdoc-main-content" class="main-content">
<div>
<h1><span class="kind-top-level-property">libName</span> top-level constant
<a href="https://dart.dev/null-safety" class="feature feature-null-safety" title="Supports the null safety language feature.">Null safety</a>
</h1></div>
<section class="multi-line-signature">
<a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a>
const <span class="name ">libName</span>
</section>
<section class="summary source-code" id="source">
<h2><span>Implementation</span></h2>
<pre class="language-dart"><code class="language-dart">const libName = &#39;dcat&#39;;</code></pre>
</section>
</div> <!-- /.main-content -->
<div id="dartdoc-sidebar-left" class="sidebar sidebar-offcanvas-left">
<header id="header-search-sidebar" class="hidden-l">
<form class="search-sidebar" role="search">
<input type="text" id="search-sidebar" autocomplete="off" disabled class="form-control typeahead" placeholder="Loading search...">
</form>
</header>
<ol class="breadcrumbs gt-separated dark hidden-l" id="sidebar-nav">
<li><a href="../index.html">dcat</a></li>
<li><a href="../dcat/dcat-library.html">dcat</a></li>
<li class="self-crumb">libName constant</li>
</ol>
<h5>dcat library</h5>
<ol>
<li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li>
<li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li>
<li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li>
<li><a href="../dcat/libName-constant.html">libName</a></li>
<li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li>
<li><a href="../dcat/cat.html">cat</a></li>
<li><a href="../dcat/printError.html">printError</a></li>
</ol>
</div><!--/.sidebar-offcanvas-left-->
<div id="dartdoc-sidebar-right" class="sidebar sidebar-offcanvas-right">
</div><!--/.sidebar-offcanvas-->
</main>
<footer>
<span class="no-break">
dcat
1.0.0
</span>
</footer>
<script src="../static-assets/highlight.pack.js?v1"></script>
<script src="../static-assets/script.js?v1"></script>
</body>
</html>

View file

@ -54,7 +54,7 @@
<span class="returntype"><a href="https://api.dart.dev/stable/2.14.3/dart-async/Future-class.html">Future</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.3/dart-core/int-class.html">int</a></span>&gt;</span></span> <span class="returntype"><a href="https://api.dart.dev/stable/2.14.3/dart-async/Future-class.html">Future</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="https://api.dart.dev/stable/2.14.3/dart-core/int-class.html">int</a></span>&gt;</span></span>
<span class="name ">printError</span>(<wbr><ol class="parameter-list"><li><span class="parameter" id="printError-param-message"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">message</span>, </span></li> <span class="name ">printError</span>(<wbr><ol class="parameter-list"><li><span class="parameter" id="printError-param-message"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">message</span>, </span></li>
<li><span class="parameter" id="printError-param-appName">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">appName</span> = <span class="default-value">libName</span>, </span></li> <li><span class="parameter" id="printError-param-appName">{<span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">appName</span> = <span class="default-value">&#39;&#39;</span>, </span></li>
<li><span class="parameter" id="printError-param-path"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">path</span> = <span class="default-value">&#39;&#39;</span>}</span></li> <li><span class="parameter" id="printError-param-path"><span class="type-annotation"><a href="https://api.dart.dev/stable/2.14.3/dart-core/String-class.html">String</a></span> <span class="parameter-name">path</span> = <span class="default-value">&#39;&#39;</span>}</span></li>
</ol>) </ol>)
@ -69,12 +69,14 @@
<section class="summary source-code" id="source"> <section class="summary source-code" id="source">
<h2><span>Implementation</span></h2> <h2><span>Implementation</span></h2>
<pre class="language-dart"><code class="language-dart">Future&lt;int&gt; printError(String message, <pre class="language-dart"><code class="language-dart">Future&lt;int&gt; printError(String message,
{String appName = libName, String path = &#39;&#39;}) async { {String appName = &#39;&#39;, String path = &#39;&#39;}) async {
if (path.isNotEmpty) { if (appName.isNotEmpty) {
stderr.writeln(&#39;$libName: $path: $message&#39;); stderr.write(&#39;$appName: &#39;);
} else {
stderr.write(&#39;$libName: $message&#39;);
} }
if (path.isNotEmpty) {
stderr.write(&#39;$path: &#39;);
}
stderr.writeln(message);
return exitFailure; return exitFailure;
}</code></pre> }</code></pre>
</section> </section>
@ -104,7 +106,6 @@
<li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#constants">Constants</a></li>
<li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li> <li><a href="../dcat/exitFailure-constant.html">exitFailure</a></li>
<li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li> <li><a href="../dcat/exitSuccess-constant.html">exitSuccess</a></li>
<li><a href="../dcat/libName-constant.html">libName</a></li>
<li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li> <li class="section-title"><a href="../dcat/dcat-library.html#functions">Functions</a></li>

View file

@ -48,6 +48,8 @@
<a href="https://github.com/ethauvin/dcat/actions/workflows/dart.yml"><img src="https://github.com/ethauvin/dcat/actions/workflows/dart.yml/badge.svg" alt="GitHub CI"></a></p> <a href="https://github.com/ethauvin/dcat/actions/workflows/dart.yml"><img src="https://github.com/ethauvin/dcat/actions/workflows/dart.yml/badge.svg" alt="GitHub CI"></a></p>
<h1 id="dcat-concatenate-files-to-standard-output">dcat: Concatenate File(s) to Standard Output</h1> <h1 id="dcat-concatenate-files-to-standard-output">dcat: Concatenate File(s) to Standard Output</h1>
<p>A <strong>cat</strong> command-line implemenation in <a href="https://dart.dev/">Dart</a>, inspired by the <a href="https://dart.dev/tutorials/server/cmdline">Write command-line apps sample code</a>.</p> <p>A <strong>cat</strong> command-line implemenation in <a href="https://dart.dev/">Dart</a>, inspired by the <a href="https://dart.dev/tutorials/server/cmdline">Write command-line apps sample code</a>.</p>
<h2 id="synopsis">Synopsis</h2>
<p><strong>dcat</strong> copies each file, or standard input if none are given, to standard output.</p>
<h2 id="command-line-usage">Command-Line Usage</h2> <h2 id="command-line-usage">Command-Line Usage</h2>
<pre class="language-sh"><code class="language-sh">dcat --help <pre class="language-sh"><code class="language-sh">dcat --help
</code></pre> </code></pre>
@ -56,19 +58,23 @@ Concatenate FILE(s) to standard output.
With no FILE, or when FILE is -, read standard input. With no FILE, or when FILE is -, read standard input.
-b, --number-nonblank number nonempty output lines, overrides -n -A, --show-all equivalent to -vET
-E, --show-ends display $ at end of each line -b, --number-nonblank number nonempty output lines, overrides -n
-h, --help display this help and exit -e, --show-nonprinting-ends equivalent to -vE
-n, --number number all output lines -E, --show-ends display $ at end of each line
-T, --show-tabs display TAB characters as ^I -h, --help display this help and exit
-s, --squeeze-blank suppress repeated empty output lines -n, --number number all output lines
--version output version information and exit -t, --show-nonprinting-tabs equivalent to -vT
-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 M- notation, except for LFD and TAB
Examples: Examples:
dcat f - g Output f's contents, then standard input, then g's contents. dcat f - g Output f's contents, then standard input, then g's contents.
dcat Copy standard input to standard output. dcat Copy standard input to standard output.
</code></pre> </code></pre>
<h2 id="compile-application">Compile Application</h2> <h2 id="compile-standalone-application">Compile Standalone Application</h2>
<h3 id="nix">*nix</h3> <h3 id="nix">*nix</h3>
<pre class="language-sh"><code class="language-sh">dart compile exe -o bin/dcat bin/dcat.dart <pre class="language-sh"><code class="language-sh">dart compile exe -o bin/dcat bin/dcat.dart
</code></pre> </code></pre>
@ -78,7 +84,8 @@ Examples:
<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> <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> <ul>
<li>No binary file support.</li> <li>No binary file support.</li>
<li>Line numbers are printed as <code>X:</code> where <code>X</code> is the line number.</li> <li>A line is considered terminated by either a <code>CR</code> (carriage return), a <code>LF</code> (line feed), a <code>CR+LF</code> sequence (DOS line ending).</li>
<li>The non-printing <code>M-^?</code> notation is always used for unicode characters.</li>
</ul> </ul>
</section> </section>
@ -90,7 +97,7 @@ Examples:
<span class="name"><a href="dcat/dcat-library.html">dcat</a></span> <span class="name"><a href="dcat/dcat-library.html">dcat</a></span>
</dt> </dt>
<dd> <dd>Library to concatenate file(s) to standard output,
</dd> </dd>
</dl> </dl>

View file

@ -1 +1 @@
[{"name":"dcat","qualifiedName":"dcat","href":"dcat/dcat-library.html","type":"library","overriddenDepth":0,"packageName":"dcat"},{"name":"cat","qualifiedName":"dcat.cat","href":"dcat/cat.html","type":"function","overriddenDepth":0,"packageName":"dcat","enclosedBy":{"name":"dcat","type":"library"}},{"name":"exitFailure","qualifiedName":"dcat.exitFailure","href":"dcat/exitFailure-constant.html","type":"top-level constant","overriddenDepth":0,"packageName":"dcat","enclosedBy":{"name":"dcat","type":"library"}},{"name":"exitSuccess","qualifiedName":"dcat.exitSuccess","href":"dcat/exitSuccess-constant.html","type":"top-level constant","overriddenDepth":0,"packageName":"dcat","enclosedBy":{"name":"dcat","type":"library"}},{"name":"libName","qualifiedName":"dcat.libName","href":"dcat/libName-constant.html","type":"top-level constant","overriddenDepth":0,"packageName":"dcat","enclosedBy":{"name":"dcat","type":"library"}},{"name":"printError","qualifiedName":"dcat.printError","href":"dcat/printError.html","type":"function","overriddenDepth":0,"packageName":"dcat","enclosedBy":{"name":"dcat","type":"library"}}] [{"name":"dcat","qualifiedName":"dcat","href":"dcat/dcat-library.html","type":"library","overriddenDepth":0,"packageName":"dcat"},{"name":"cat","qualifiedName":"dcat.cat","href":"dcat/cat.html","type":"function","overriddenDepth":0,"packageName":"dcat","enclosedBy":{"name":"dcat","type":"library"}},{"name":"exitFailure","qualifiedName":"dcat.exitFailure","href":"dcat/exitFailure-constant.html","type":"top-level constant","overriddenDepth":0,"packageName":"dcat","enclosedBy":{"name":"dcat","type":"library"}},{"name":"exitSuccess","qualifiedName":"dcat.exitSuccess","href":"dcat/exitSuccess-constant.html","type":"top-level constant","overriddenDepth":0,"packageName":"dcat","enclosedBy":{"name":"dcat","type":"library"}},{"name":"printError","qualifiedName":"dcat.printError","href":"dcat/printError.html","type":"function","overriddenDepth":0,"packageName":"dcat","enclosedBy":{"name":"dcat","type":"library"}}]

View file

@ -2,33 +2,35 @@
// Use of this source code is governed by a BSD-style license that can be found // Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file. // in the LICENSE file.
/// Library to concatenate file(s) to standard output,
library dcat; library dcat;
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
const libName = 'dcat';
const exitFailure = 1; const exitFailure = 1;
const exitSuccess = 0; const exitSuccess = 0;
/// Concatenates files in [paths] to [stdout] /// Concatenates files in [paths] to [stdout]
/// ///
/// The parameters are similar to the [GNU cat utility](https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation). /// The parameters are similar to the [GNU cat utility](https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation).
/// Specify a [log] for debugging purpose. /// Specify a [log] for debugging or testing purpose.
Future<int> cat(List<String> paths, Future<int> cat(List<String> paths,
{List<String>? log, {String appName = '',
List<String>? log,
bool showEnds = false, bool showEnds = false,
bool numberNonBlank = false, bool numberNonBlank = false,
bool showLineNumbers = false, bool showLineNumbers = false,
bool showTabs = false, bool showTabs = false,
bool squeezeBlank = false}) async { bool squeezeBlank = false,
bool showNonPrinting = false}) async {
var lineNumber = 1; var lineNumber = 1;
var returnCode = 0; var returnCode = 0;
log?.clear(); log?.clear();
if (paths.isEmpty) { if (paths.isEmpty) {
final lines = await _readStdin(); final lines = await _readStdin();
await _writeLines(lines, lineNumber, log, showEnds, showLineNumbers, await _writeLines(lines, lineNumber, log, showEnds, showLineNumbers,
numberNonBlank, showTabs, squeezeBlank); numberNonBlank, showTabs, squeezeBlank, showNonPrinting);
} else { } else {
for (final path in paths) { for (final path in paths) {
try { try {
@ -40,8 +42,16 @@ Future<int> cat(List<String> paths,
.bind(File(path).openRead()) .bind(File(path).openRead())
.transform(const LineSplitter()); .transform(const LineSplitter());
} }
lineNumber = await _writeLines(lines, lineNumber, log, showEnds, lineNumber = await _writeLines(
showLineNumbers, numberNonBlank, showTabs, squeezeBlank); lines,
lineNumber,
log,
showEnds,
showLineNumbers,
numberNonBlank,
showTabs,
squeezeBlank,
showNonPrinting);
} on FileSystemException catch (e) { } on FileSystemException catch (e) {
final String? osMessage = e.osError?.message; final String? osMessage = e.osError?.message;
final String message; final String message;
@ -50,25 +60,63 @@ Future<int> cat(List<String> paths,
} else { } else {
message = e.message; message = e.message;
} }
returnCode = await printError(message, path: path); returnCode = await printError(message, appName: appName, path: path);
} on FormatException { } on FormatException {
returnCode = await printError('Binary file not supported.', path: path); returnCode = await printError('Binary file not supported.',
appName: appName, path: path);
} catch (e) { } catch (e) {
returnCode = await printError(e.toString(), path: path); returnCode =
await printError(e.toString(), appName: appName, path: path);
} }
} }
} }
return returnCode; return returnCode;
} }
/// Parses line with non-printing characters.
Future<String> _parseNonPrinting(String line, bool showTabs) async {
final sb = StringBuffer();
for (var ch in line.runes) {
if (ch >= 32) {
if (ch < 127) {
sb.writeCharCode(ch);
} else if (ch == 127) {
sb.write('^?');
} else {
sb.write('M-');
if (ch >= 128 + 32) {
if (ch < 128 + 127) {
sb.writeCharCode(ch - 128);
} else {
sb.write('^?');
}
} else {
sb
..write('^')
..writeCharCode(ch - 128 + 64);
}
}
} else if (ch == 9 && !showTabs) {
sb.write('\t');
} else {
sb
..write('^')
..writeCharCode(ch + 64);
}
}
return sb.toString();
}
/// Prints the [appName], [path] and error [message] to [stderr]. /// Prints the [appName], [path] and error [message] to [stderr].
Future<int> printError(String message, Future<int> printError(String message,
{String appName = libName, String path = ''}) async { {String appName = '', String path = ''}) async {
if (path.isNotEmpty) { if (appName.isNotEmpty) {
stderr.writeln('$libName: $path: $message'); stderr.write('$appName: ');
} else {
stderr.write('$libName: $message');
} }
if (path.isNotEmpty) {
stderr.write('$path: ');
}
stderr.writeln(message);
return exitFailure; return exitFailure;
} }
@ -83,7 +131,8 @@ Future<int> _writeLines(Stream<String> lines, int lineNumber,
bool showLineNumbers = false, bool showLineNumbers = false,
bool showNonBlank = false, bool showNonBlank = false,
bool showTabs = false, bool showTabs = false,
bool squeezeBlank = false]) async { bool squeezeBlank = false,
bool showNonPrinting = false]) async {
var emptyLine = 0; var emptyLine = 0;
final sb = StringBuffer(); final sb = StringBuffer();
await for (final line in lines) { await for (final line in lines) {
@ -95,14 +144,18 @@ Future<int> _writeLines(Stream<String> lines, int lineNumber,
} else { } else {
emptyLine = 0; emptyLine = 0;
} }
if (showNonBlank || showLineNumbers) { if (showLineNumbers || (showNonBlank && line.isNotEmpty)) {
sb.write('${lineNumber++}: '); sb.write('${lineNumber++} '.padLeft(8));
} }
if (showTabs) {
if (showNonPrinting) {
sb.write(await _parseNonPrinting(line, showTabs));
} else if (showTabs) {
sb.write(line.replaceAll('\t', '^I')); sb.write(line.replaceAll('\t', '^I'));
} else { } else {
sb.write(line); sb.write(line);
} }
if (showEnds) { if (showEnds) {
sb.write('\$'); sb.write('\$');
} }

View file

@ -9,6 +9,6 @@ environment:
dev_dependencies: dev_dependencies:
lints: ^1.0.0 lints: ^1.0.0
test: ^1.18.2 test: ^1.18.2
dependencies: dependencies:
args: ^2.3.0 args: ^2.3.0
indent: ^2.0.0 indent: ^2.0.0

View file

@ -7,96 +7,139 @@ void main() {
final List<String> log = []; final List<String> log = [];
int exitCode; int exitCode;
test('Test Help', () async { group('app', () {
expect(app.main(['-h']), completion(equals(0))); test('Test Help', () async {
expect(app.main(['--help']), completion(equals(0))); expect(app.main(['-h']), completion(equals(0)));
exitCode = await app.main(['-h']); expect(app.main(['--help']), completion(equals(0)));
expect(exitCode, equals(exitSuccess)); exitCode = await app.main(['-h']);
expect(exitCode, equals(exitSuccess));
});
test('Test --version', () async {
expect(app.main(['--version']), completion(equals(0)));
exitCode = await app.main(['--version']);
expect(exitCode, equals(exitSuccess));
});
test('Test directory', () async {
exitCode = await app.main(['bin']);
expect(exitCode, equals(exitFailure));
});
test('Test missing file', () async {
exitCode = await app.main(['foo']);
expect(exitCode, equals(exitFailure), reason: 'foo not found');
exitCode = await app.main(['bin/dcat.dart', 'foo']);
expect(exitCode, equals(exitFailure), reason: 'one missing file');
});
}); });
test('Test --version', () async { group('lib', () {
expect(app.main(['--version']), completion(equals(0))); test('Test cat source', () async {
exitCode = await app.main(['--version']); await cat(['bin/dcat.dart'], log: log);
expect(exitCode, equals(exitSuccess)); expect(log.isEmpty, false, reason: 'log is empty');
}); expect(log.first, startsWith('// Copyright (c)'),
reason: 'has copyright');
expect(log.last, equals('}'));
});
test('Test directory', () async { test('Test cat -n source', () async {
exitCode = await app.main(['bin']); exitCode = await cat(['bin/dcat.dart'], log: log, showLineNumbers: true);
expect(exitCode, equals(exitFailure)); expect(exitCode, 0, reason: 'result code is 0');
}); expect(log.first, startsWith(' 1 // Copyright (c)'),
reason: 'has copyright');
test('Test missing file', () async { expect(log.last, endsWith(' }'), reason: 'last line');
exitCode = await app.main(['foo']); for (final String line in log) {
expect(exitCode, equals(exitFailure), reason: 'foo not found'); expect(line, matches('^ +\\d+ .*'), reason: 'has line number');
exitCode = await app.main(['bin/dcat.dart', 'foo']);
expect(exitCode, equals(exitFailure), reason: 'one missing file');
});
test('Test cat source', () async {
await cat(['bin/dcat.dart'], log: log);
expect(log.isEmpty, false, reason: 'log is empty');
expect(log.first, startsWith('// Copyright (c)'), reason: 'has copyright');
expect(log.last, equals('}'));
});
test('Test cat -n source', () async {
exitCode = await cat(['bin/dcat.dart'], log: log, showLineNumbers: true);
expect(exitCode, 0, reason: 'result code is 0');
expect(log.first, startsWith('1: // Copyright (c)'),
reason: 'has copyright');
expect(log.last, endsWith(': }'), reason: 'last line');
for (final String line in log) {
expect(line, matches('^\\d+: .*'), reason: 'has line number');
}
});
test('Test cat -E', () async {
await cat(['test/test.txt'], log: log, showEnds: true);
var hasBlank = false;
for (final String line in log) {
expect(line, endsWith('\$'));
if (line == '\$') {
hasBlank = true;
} }
} });
expect(hasBlank, true, reason: 'has blank line');
});
test('Test cat -bE', () async { test('Test cat source test', () async {
await cat(['test/test.txt'], await cat(['bin/dcat.dart', 'test/test.txt'], log: log);
log: log, numberNonBlank: true, showEnds: true); expect(log.length, greaterThan(10), reason: 'more than 10 lines');
var hasBlank = false; expect(log.first, startsWith('// Copyright'),
for (final String line in log) { reason: 'start with copyright');
expect(line, endsWith('\$')); expect(log.last, endsWith(''), reason: 'end with checkmark');
if (line.contains(RegExp(r'^\d+: .*\$$'))) { });
hasBlank = true;
}
}
expect(hasBlank, true, reason: 'has blank line');
});
test('Test cat -T', () async { test('Test cat -E', () async {
await cat(['test/test.txt'], log: log, showTabs: true); await cat(['test/test.txt'], log: log, showEnds: true);
var hasTab = false; var hasBlank = false;
for (final String line in log) { for (final String line in log) {
if (line.startsWith('^I')) { expect(line, endsWith('\$'));
hasTab = true; if (line == '\$') {
break; hasBlank = true;
}
} }
} expect(hasBlank, true, reason: 'has blank line');
expect(hasTab, true, reason: 'has tab'); expect(log.last, endsWith('\$'), reason: 'has unicode');
}); });
test('Test cat -s', () async { test('Test cat -bE', () async {
await cat(['test/test.txt'], log: log, squeezeBlank: true); await cat(['test/test.txt'],
var hasSqueeze = true; log: log, numberNonBlank: true, showEnds: true);
var prevLine = 'foo'; var hasBlank = false;
for (final String line in log) { for (final String line in log) {
if (line == prevLine) { expect(line, endsWith('\$'));
hasSqueeze = false; if (line.contains(RegExp(r'^ +\d+ .*\$$'))) {
hasBlank = true;
}
} }
prevLine = line; expect(hasBlank, true, reason: 'has blank line');
} });
expect(hasSqueeze, true, reason: 'has squeeze');
test('Test cat -T', () async {
await cat(['test/test.txt'], log: log, showTabs: true);
var hasTab = false;
for (final String line in log) {
if (line.startsWith('^I')) {
hasTab = true;
break;
}
}
expect(hasTab, true, reason: 'has tab');
});
test('Test cat -s', () async {
await cat(['test/test.txt'], log: log, squeezeBlank: true);
var hasSqueeze = true;
var prevLine = 'foo';
for (final String line in log) {
if (line == prevLine) {
hasSqueeze = false;
}
prevLine = line;
}
expect(hasSqueeze, true, reason: 'has squeeze');
});
test('Test cat -A', () async {
await cat(['test/test.txt'],
log: log, showNonPrinting: true, showEnds: true, showTabs: true);
expect(log.last, equals('^I^A^B^C^DM-^?\$'));
});
test('Test cat -t', () async {
await cat(['test/test.txt'],
log: log, showNonPrinting: true, showTabs: true);
expect(log.last, equals('^I^A^B^C^DM-^?'));
});
test('Test cat-Abs', () async {
await cat(['test/test.txt'],
log: log,
showNonPrinting: true,
showEnds: true,
showTabs: true,
numberNonBlank: true,
squeezeBlank: true);
var blankLines = 0;
for (final String line in log) {
if (line == '\$') {
blankLines++;
}
}
expect(blankLines, 2, reason: 'only 2 blank lines.');
});
}); });
} }

View file

@ -4,4 +4,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor i
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.