Initial commit

master v0.1.0
Jason T. Lenz 3 years ago
commit 56b73ae250
  1. 2
      .gitignore
  2. 24
      LICENSE
  3. 150
      Makefile
  4. 128
      README.md
  5. 58
      doc/chuf.1
  6. 44
      doc/chuf.1.scd
  7. 8
      go.mod
  8. 4
      go.sum
  9. 108
      main.go
  10. 54
      template/Makefile.got
  11. 120
      template/README.md.got
  12. 14
      tests/chuf_test.go
  13. 1
      tests/invalid-bad-executable/tCmd
  14. 1
      tests/invalid-bad-executable/tExit
  15. 1
      tests/invalid-bad-executable/tStderr
  16. 1
      tests/invalid-bad-executable/tStdin
  17. 0
      tests/invalid-bad-executable/tStdout
  18. 1
      tests/invalid-missing-end-identifier/tCmd
  19. 1
      tests/invalid-missing-end-identifier/tExit
  20. 1
      tests/invalid-missing-end-identifier/tStderr
  21. 1
      tests/invalid-missing-end-identifier/tStdin
  22. 1
      tests/invalid-missing-end-identifier/tStdout
  23. 1
      tests/invalid-null-chunk-identifier/tCmd
  24. 1
      tests/invalid-null-chunk-identifier/tExit
  25. 1
      tests/invalid-null-chunk-identifier/tStderr
  26. 1
      tests/invalid-null-chunk-identifier/tStdin
  27. 0
      tests/invalid-null-chunk-identifier/tStdout
  28. 1
      tests/invalid-null-executable/tCmd
  29. 1
      tests/invalid-null-executable/tExit
  30. 1
      tests/invalid-null-executable/tStderr
  31. 1
      tests/invalid-null-executable/tStdin
  32. 0
      tests/invalid-null-executable/tStdout
  33. 1
      tests/invalid-wrong-number-of-arguments/tCmd
  34. 1
      tests/invalid-wrong-number-of-arguments/tExit
  35. 6
      tests/invalid-wrong-number-of-arguments/tStderr
  36. 1
      tests/invalid-wrong-number-of-arguments/tStdin
  37. 0
      tests/invalid-wrong-number-of-arguments/tStdout
  38. 1
      tests/valid-empty-chunk/tCmd
  39. 1
      tests/valid-empty-chunk/tExit
  40. 0
      tests/valid-empty-chunk/tStderr
  41. 1
      tests/valid-empty-chunk/tStdin
  42. 1
      tests/valid-empty-chunk/tStdout
  43. 1
      tests/valid-empty-input/tCmd
  44. 1
      tests/valid-empty-input/tExit
  45. 0
      tests/valid-empty-input/tStderr
  46. 0
      tests/valid-empty-input/tStdin
  47. 0
      tests/valid-empty-input/tStdout
  48. 1
      tests/valid-multiple-chunks/tCmd
  49. 1
      tests/valid-multiple-chunks/tExit
  50. 0
      tests/valid-multiple-chunks/tStderr
  51. 2
      tests/valid-multiple-chunks/tStdin
  52. 2
      tests/valid-multiple-chunks/tStdout
  53. 1
      tests/valid-no-chunk/tCmd
  54. 1
      tests/valid-no-chunk/tExit
  55. 0
      tests/valid-no-chunk/tStderr
  56. 1
      tests/valid-no-chunk/tStdin
  57. 1
      tests/valid-no-chunk/tStdout
  58. 1
      tests/valid-simple/tCmd
  59. 1
      tests/valid-simple/tExit
  60. 0
      tests/valid-simple/tStderr
  61. 1
      tests/valid-simple/tStdin
  62. 1
      tests/valid-simple/tStdout

2
.gitignore vendored

@ -0,0 +1,2 @@
chuf
*.result

@ -0,0 +1,24 @@
Copyright (c) 2020 Jason T. Lenz. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,150 @@
.POSIX:
PNAME = chuf
RTEMPLATE ?= ../repo-template
all: goUtil doc
doc: docMain docMan
clean: cleanGoUtil cleanCheck
cleanDoc: cleanDocMain cleanDocMan
install: installGoUtil installMan
uninstall: uninstallGoUtil uninstallMan
.DEFAULT_GOAL := all
.PHONY: all doc clean cleanDoc install uninstall
#---Helper Macros to Remove Files---
RMDIR_IF_EMPTY := sh -c '\
if test -d $$0 && ! ls -1qA $$0 | grep -q . ; then \
rmdir $$0; \
fi'
RM ?= rm -f
#---Generate Golang Utility---
PREFIX ?= /usr/local
_INSTDIR = $(DESTDIR)$(PREFIX)
BINDIR ?= $(_INSTDIR)/bin
GO ?= go
GOFLAGS ?=
VERSION != git describe --first-parent 2> /dev/null
GOSRC != find . -name '*.go'
goUtil: $(PNAME)
$(PNAME): $(GOSRC)
$(GO) build $(GOFLAGS) \
-ldflags "-X main.Version=$(VERSION)" \
-o $@
cleanGoUtil:
$(RM) $(PNAME)
installGoUtil: $(PNAME)
strip $(PNAME)
mkdir -m755 -p $(BINDIR)
install -m755 $(PNAME) $(BINDIR)/$(PNAME)
uninstallGoUtil:
$(RM) $(BINDIR)/$(PNAME)
$(RMDIR_IF_EMPTY) $(BINDIR)
.PHONY: goUtil cleanGoUtil installGoUtil uninstallGoUtil
#---Generate Man Page(s)---
.SUFFIXES:
.SUFFIXES: .1 .1.scd
PREFIX ?= /usr/local
_INSTDIR = $(DESTDIR)$(PREFIX)
MANDIR ?= $(_INSTDIR)/share/man
DOCMAN := doc/$(PNAME).1
.1.scd.1:
scdoc < $< > $@
docMan: $(DOCMAN)
cleanDocMan:
$(RM) $(DOCMAN)
installMan: $(DOCMAN)
mkdir -m755 -p $(MANDIR)/man1
install -m644 doc/$(PNAME).1 $(MANDIR)/man1/$(PNAME).1
uninstallMan:
$(RM) $(MANDIR)/man1/$(PNAME).1
$(RMDIR_IF_EMPTY) $(MANDIR)/man1
$(RMDIR_IF_EMPTY) $(MANDIR)
.PHONY: installMan uninstallMan
#---Test/Check Section---
TESTDIR = tests
check: $(PNAME)
cd $(TESTDIR) && go test -v
cleanCheck:
find $(TESTDIR) -name '*.result' -delete
.PHONY: check cleanCheck
#---Generate Main Documents---
DOCMAIN := README.md LICENSE
README.md: template/README.md.got
pgot -i ":$(RTEMPLATE)" -o $@ $<
LICENSE: $(RTEMPLATE)/LICENSE.src/BSD-2-clause.got
pgot -i ":$(RTEMPLATE)" -o $@ $<
docMain: $(DOCMAIN)
cleanDocMain:
$(RM) $(DOCMAIN)
.PHONY: docMain, cleanDocMain
#---Generate Makefile---
Makefile: template/Makefile.got
pgot -i ":$(RTEMPLATE)" -o $@ $<
mkFile: Makefile
regenMkFile:
pgot -i ":$(RTEMPLATE)" -o Makefile template/Makefile.got
.PHONY: mkFile regenMkFile
#---Lint Helper Target---
lint:
@find . -path ./.git -prune -or \
-type f -and -not -name 'Makefile' \
-exec grep -Hn '<no value>' '{}' ';'
#---TODO Helper Target---
todo:
@find . -path ./.git -prune -or \
-type f -and -not -name 'Makefile' \
-exec grep -Hn TODO '{}' ';'
# vim:set noet tw=80:.POSIX:

@ -0,0 +1,128 @@
# chuf
**chuf** is a command line utility that transforms stdin to stdout by sending
predefined chunks through the specified _FILTER_. A chunk is defined by a
_BEGIN_ and _END_ byte sequence within the stream. Anything not within a chunk
is passed along unmodified.
## Synopsis
```text
chuf BEGIN END FILTER
```
_BEGIN_ : Byte sequence designating beginning of chunk
_END_ : Byte sequence designating end of chunk
_FILTER_ : Command filter to transform chunk. A multiple parameter command
filter must be enclosed in quotes.
## Example 1 - Simple text markup using unix "tr" command.
```text
$ echo "the quick {U}brown fox{R} jumped over the lazy dog" | \\
chuf {U} {R} "tr [:lower:] [:upper:]"
the quick BROWN FOX jumped over the lazy dog
```
## Example 2 - Expand markdown within html document.
```text
$ cat example.mdhtml
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<md>
# Markdown table example
| Name | Hobby | Age |
| ------ |:------------:| -----:|
| Bob | golfing | 18 |
| Monica | programming | 32 |
</md>
</body>
</html>
$ chuf '<md>' '</md>' markdown < example.mdhtml
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<h1>Markdown table example</h1>
<table>
<thead>
<tr>
<th> Name </th>
<th style="text-align:center;"> Hobby </th>
<th style="text-align:right;"> Age </th>
</tr>
</thead>
<tbody>
<tr>
<td> Bob </td>
<td style="text-align:center;"> golfing </td>
<td style="text-align:right;"> 18 </td>
</tr>
<tr>
<td> Monica </td>
<td style="text-align:center;"> programming </td>
<td style="text-align:right;"> 32 </td>
</tr>
</tbody>
</table>
</body>
</html>
```
## Compiling from source
### Dependencies
* Go compiler (v1.12 or later).
* Go package [chunkio](https://git.lenzplace.org/lenzj/chunkio)
* Go package [testcli](https://git.lenzplace.org/lenzj/testcli) to run tests.
* [scdoc](https://git.sr.ht/~sircmpwn/scdoc/) utility to generate the man page.
Only needed if changes to man page source (chuf.1.scd) are made.
* [pgot](https://git.lenzplace.org/lenzj/pgot) utility to process files in the templates sub
folder. Only needed if changes to README.md, LICENCE, Makefile etc. are
needed.
### Installing
```text
$ make
# make install
```
## Running the tests
```text
$ make check
```
## Contributing
If you have a bugfix, update, issue or feature enhancement the best way to reach
me is by following the instructions in the link below. Thank you!
<https://blog.lenzplace.org/about/contact.html>
## Versioning
I follow the [SemVer](http://semver.org/) strategy for versioning. The latest
version is listed in the [releases](/lenzj/chuf/releases) section.
## License
This project is licensed under a BSD two clause license - see the
[LICENSE](LICENSE) file for details.
<!-- vim:set ts=4 sw=4 et tw=80: -->

@ -0,0 +1,58 @@
.\" Generated by scdoc 1.9.7
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.nh
.ad l
.\" Begin generated content:
.TH "chuf" "1" "2020-04-26"
.P
.SH NAME
.P
chuf - transform a stream by sending specific chunks through a filter
.P
.SH SYNOPSIS
.P
\fBchuf\fR \fIBEGIN\fR \fIEND\fR \fIFILTER\fR
.P
.SH DESCRIPTION
.P
\fBchuf\fR is a command line utility that transforms stdin to stdout by sending
predefined chunks through the specified \fIFILTER\fR. A chunk is defined by a
\fIBEGIN\fR and \fIEND\fR byte sequence within the stream. Anything not within a chunk
is passed along unmodified.
.P
\fIBEGIN\fR
.RS 4
Byte sequence designating beginning of chunk
.P
.RE
\fIEND\fR
.RS 4
Byte sequence designating end of chunk
.P
.RE
\fIFILTER\fR
.RS 4
Command filter to transform chunk. A multiple parameter command filter
must be enclosed in quotes.
.P
.RE
.SH EXAMPLE
.P
$ \fBecho "the quick {U}brown fox{R} jumped over the lazy dog" | \\
.br
chuf {U} {R} "tr [:lower:] [:upper:]"\fR
.br
the quick BROWN FOX jumped over the lazy dog
.P
.SH BUGS
.P
Bug reporting instructions:
.br
<https://blog.lenzplace.org/about/contact.html>
.P
.SH SEE ALSO
.P
Website for chuf:
.br
<https://git.lenzplace.org/lenzj/chuf>

@ -0,0 +1,44 @@
chuf(1)
; This is a scdoc source file for generating a man page
; See https://git.sr.ht/~sircmpwn/scdoc
# NAME
chuf - transform a stream by sending specific chunks through a filter
# SYNOPSIS
*chuf* _BEGIN_ _END_ _FILTER_
# DESCRIPTION
*chuf* is a command line utility that transforms stdin to stdout by sending
predefined chunks through the specified _FILTER_. A chunk is defined by a
_BEGIN_ and _END_ byte sequence within the stream. Anything not within a chunk
is passed along unmodified.
_BEGIN_
Byte sequence designating beginning of chunk
_END_
Byte sequence designating end of chunk
_FILTER_
Command filter to transform chunk. A multiple parameter command filter
must be enclosed in quotes.
# EXAMPLE
$ *echo "the quick {U}brown fox{R} jumped over the lazy dog" | \\++
chuf {U} {R} "tr [:lower:] [:upper:]"*++
the quick BROWN FOX jumped over the lazy dog
# BUGS
Bug reporting instructions:++
<https://blog.lenzplace.org/about/contact.html>
# SEE ALSO
Website for chuf:++
<https://git.lenzplace.org/lenzj/chuf>

@ -0,0 +1,8 @@
module git.lenzplace.org/lenzj/chuf
go 1.12
require (
git.lenzplace.org/lenzj/chunkio v0.1.3
git.lenzplace.org/lenzj/testcli v0.2.2
)

@ -0,0 +1,4 @@
git.lenzplace.org/lenzj/chunkio v0.1.3 h1:FZ7s6l1QzIkBVbjFjyirPjwvnd3lpynjmOm58adGuLA=
git.lenzplace.org/lenzj/chunkio v0.1.3/go.mod h1:eWvJdI3CVgeiZhv721QHC86KIPTFH08utgPHJ8uJAl0=
git.lenzplace.org/lenzj/testcli v0.2.2 h1:Kh/GBKcgc7VbU45vPnlXEA480XKAbbidftomLn7l9iw=
git.lenzplace.org/lenzj/testcli v0.2.2/go.mod h1:qy+OAujsHAbWf3+jwZgVgqN7SbDUKg10HSY/HXjVhUU=

@ -0,0 +1,108 @@
package main
import (
"flag"
"fmt"
"io"
"log"
"git.lenzplace.org/lenzj/chunkio"
"os"
"os/exec"
"path/filepath"
"strings"
)
func Parse(in io.Reader, out io.Writer, start, end []byte, filtSlice []string) error {
cio := chunkio.NewReader(in)
for cio.GetErr() == nil {
var cmd *exec.Cmd
if len(filtSlice) > 1 {
cmd = exec.Command(filtSlice[0], filtSlice[1:]...)
} else {
cmd = exec.Command(filtSlice[0])
}
cmd.Stdin = cio
cmd.Stdout = out
cio.SetKey(start)
_, err := io.Copy(out, cio)
if err == io.ErrUnexpectedEOF {
break
} else if err != nil {
return err
}
cio.Reset()
cio.SetKey(end)
if err = cmd.Run(); err != nil {
return err
}
cio.Reset()
}
return nil
}
var Version string
func main() {
// Get application name as executed from command prompt
appName := filepath.Base(os.Args[0])
// Set up formatting for error messages
log.SetFlags(0)
log.SetPrefix(appName + ": ")
// Parse command line
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(),
"Usage: %s BEGIN END FILTER\n"+
"Transform sections of stdin by passing through a filter program.\n"+
"Sections are defined by user selected BEGIN and END identifiers.\n"+
"Options:\n", appName)
flag.PrintDefaults()
}
var vflag = flag.Bool("v", false, "display " + appName + " version")
flag.Parse()
// Display application version if requested
if *vflag {
fmt.Println(appName + " " + Version)
os.Exit(0)
}
var (
input io.Reader
output io.Writer
err error
filtStart, filtEnd string
filtSlice []string
)
switch flag.NArg() {
case 3:
filtStart = flag.Arg(0)
if len(filtStart) == 0 {
log.Fatalln("BEGIN chunk identifier must have length greater than zero")
}
filtEnd = flag.Arg(1)
if len(filtEnd) == 0 {
log.Fatalln("END chunk identifier must have length greater than zero")
}
filtSlice = strings.Split(flag.Arg(2), " ")
if _, err = exec.LookPath(filtSlice[0]); err != nil {
log.Fatalln(err)
}
default:
log.Println("incorrect number of arguments")
flag.Usage()
os.Exit(1)
}
input = os.Stdin
output = os.Stdout
err = Parse(input, output, []byte(filtStart), []byte(filtEnd), filtSlice)
if err != nil {
log.Fatalln(err)
}
}

@ -0,0 +1,54 @@
;;;
{
"pgotInclude" : [
"global.got",
"Makefile.src/mk-rm.got",
"Makefile.src/mk-goUtil.got",
"Makefile.src/mk-docMan1.got",
"Makefile.src/mk-testcli.got",
"Makefile.src/mk-docMain.got",
"Makefile.src/mk-mkFile.got",
"Makefile.src/mk-lint.got",
"Makefile.src/mk-todo.got"
]
}
;;;
.POSIX:
PNAME = chuf
RTEMPLATE ?= ../repo-template
all: goUtil doc
doc: docMain docMan
clean: cleanGoUtil cleanCheck
cleanDoc: cleanDocMain cleanDocMan
install: installGoUtil installMan
uninstall: uninstallGoUtil uninstallMan
.DEFAULT_GOAL := all
.PHONY: all doc clean cleanDoc install uninstall
{{template "mk-rm" .}}
{{template "mk-goUtil" .}}
{{template "mk-docMan1" .}}
{{template "mk-testcli" .}}
{{template "mk-docMain" .}}
{{template "mk-mkFile" .}}
{{template "mk-lint" .}}
{{template "mk-todo" .}}
# vim:set noet tw=80:.POSIX:

@ -0,0 +1,120 @@
;;;
{
"rname":"chuf",
"pgotInclude": [ "README.src/all.got" ]
}
;;;
# {{.rname}}
**{{.rname}}** is a command line utility that transforms stdin to stdout by sending
predefined chunks through the specified _FILTER_. A chunk is defined by a
_BEGIN_ and _END_ byte sequence within the stream. Anything not within a chunk
is passed along unmodified.
## Synopsis
```text
{{.rname}} BEGIN END FILTER
```
_BEGIN_ : Byte sequence designating beginning of chunk
_END_ : Byte sequence designating end of chunk
_FILTER_ : Command filter to transform chunk. A multiple parameter command
filter must be enclosed in quotes.
## Example 1 - Simple text markup using unix "tr" command.
```text
$ echo "the quick {U}brown fox{R} jumped over the lazy dog" | \\
{{.rname}} {U} {R} "tr [:lower:] [:upper:]"
the quick BROWN FOX jumped over the lazy dog
```
## Example 2 - Expand markdown within html document.
```text
$ cat example.mdhtml
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<md>
# Markdown table example
| Name | Hobby | Age |
| ------ |:------------:| -----:|
| Bob | golfing | 18 |
| Monica | programming | 32 |
</md>
</body>
</html>
$ {{.rname}} '<md>' '</md>' markdown < example.mdhtml
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<h1>Markdown table example</h1>
<table>
<thead>
<tr>
<th> Name </th>
<th style="text-align:center;"> Hobby </th>
<th style="text-align:right;"> Age </th>
</tr>
</thead>
<tbody>
<tr>
<td> Bob </td>
<td style="text-align:center;"> golfing </td>
<td style="text-align:right;"> 18 </td>
</tr>
<tr>
<td> Monica </td>
<td style="text-align:center;"> programming </td>
<td style="text-align:right;"> 32 </td>
</tr>
</tbody>
</table>
</body>
</html>
```
## Compiling from source
### Dependencies
* Go compiler (v1.12 or later).
* Go package [chunkio]({{.repUrl}}chunkio)
* Go package [testcli]({{.repUrl}}testcli) to run tests.
* [scdoc](https://git.sr.ht/~sircmpwn/scdoc/) utility to generate the man page.
Only needed if changes to man page source ({{.rname}}.1.scd) are made.
* [pgot]({{.repUrl}}pgot) utility to process files in the templates sub
folder. Only needed if changes to README.md, LICENCE, Makefile etc. are
needed.
### Installing
```text
$ make
# make install
```
## Running the tests
```text
$ make check
```
{{template "rd-contributing" .}}
{{template "rd-versioning" .}}
{{template "rd-license" .}}
<!-- vim:set ts=4 sw=4 et tw=80: -->

@ -0,0 +1,14 @@
// Copyright (c) 2019 Jason T. Lenz. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
package chuf_test
import (
"git.lenzplace.org/lenzj/testcli"
"testing"
)
func TestChuf(t *testing.T) {
testcli.RunTests(t, "../chuf")
}

@ -0,0 +1 @@
{{.cli}} {U} {L} "non_existent_executable_9869329" < tStdin

@ -0,0 +1 @@
chuf: exec: "non_existent_executable_9869329": executable file not found in $PATH

@ -0,0 +1 @@
the quick {U}brown fox jumps over the lazy dog

@ -0,0 +1 @@
{{.cli}} {U} {L} "tr [:lower:] [:upper:]" < tStdin

@ -0,0 +1 @@
the quick {U}brown fox jumps over the lazy dog

@ -0,0 +1 @@
the quick BROWN FOX JUMPS OVER THE LAZY DOG

@ -0,0 +1 @@
{{.cli}} "" {L} "ls" < tStdin

@ -0,0 +1 @@
chuf: BEGIN chunk identifier must have length greater than zero

@ -0,0 +1 @@
the quick {U}brown fox jumps over the lazy dog

@ -0,0 +1 @@
{{.cli}} {U} {L} "" < tStdin

@ -0,0 +1 @@
chuf: exec: "": executable file not found in $PATH

@ -0,0 +1 @@
the quick {U}brown fox jumps over the lazy dog

@ -0,0 +1,6 @@
chuf: incorrect number of arguments
Usage: chuf BEGIN END FILTER
Transform sections of stdin by passing through a filter program.
Sections are defined by user selected BEGIN and END identifiers.
Options:
-v display chuf version

@ -0,0 +1 @@
the quick {U}brown fox jumps over the lazy dog

@ -0,0 +1 @@
{{.cli}} {U} {L} "tr [:lower:] [:upper:]" < tStdin

@ -0,0 +1 @@
the quick {U}{L}brown fox jumps over the lazy dog

@ -0,0 +1 @@
the quick brown fox jumps over the lazy dog

@ -0,0 +1 @@
{{.cli}} {U} {L} "tr [:lower:] [:upper:]" < tStdin

@ -0,0 +1 @@
{{.cli}} {U} {L} "tr [:lower:] [:upper:]" < tStdin

@ -0,0 +1,2 @@
the quick {U}brown fox jumps{L} over the lazy dog
the quick brown fox {U}jumps over{L} the lazy dog

@ -0,0 +1,2 @@
the quick BROWN FOX JUMPS over the lazy dog
the quick brown fox JUMPS OVER the lazy dog

@ -0,0 +1 @@
{{.cli}} {U} {L} "tr [:lower:] [:upper:]" < tStdin

@ -0,0 +1 @@
the quick brown fox jumps over the lazy dog

@ -0,0 +1 @@
the quick brown fox jumps over the lazy dog

@ -0,0 +1 @@
{{.cli}} {U} {L} "tr [:lower:] [:upper:]" < tStdin

@ -0,0 +1 @@
the quick {U}brown fox jumps{L} over the lazy dog

@ -0,0 +1 @@
the quick BROWN FOX JUMPS over the lazy dog
Loading…
Cancel
Save