Repository: Code-Hex/Neo-cowsay
Branch: master
Commit: f68c20f068c2
Files: 123
Total size: 112.2 KB
Directory structure:
gitextract_va4xlec5/
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── dependabot.yml
│ └── workflows/
│ ├── lgtm.yml
│ ├── main.yml
│ └── release.yml
├── .gitignore
├── .goreleaser.yml
├── LICENSE
├── Makefile
├── README.md
├── balloon.go
├── bin/
│ └── .gitkeep
├── cmd/
│ ├── cmd.go
│ ├── cowsay/
│ │ └── main.go
│ ├── cowthink/
│ │ └── main.go
│ ├── go.mod
│ ├── go.sum
│ ├── internal/
│ │ ├── cli/
│ │ │ ├── cli.go
│ │ │ └── cli_test.go
│ │ ├── screen/
│ │ │ ├── buffer.go
│ │ │ └── screen.go
│ │ └── super/
│ │ └── supercow.go
│ └── testdata/
│ ├── cowsay/
│ │ ├── W_option.txt
│ │ ├── b_option.txt
│ │ ├── d_option.txt
│ │ ├── eyes_option.txt
│ │ ├── f_tux_option.txt
│ │ ├── g_option.txt
│ │ ├── n_option.txt
│ │ ├── p_option.txt
│ │ ├── s_option.txt
│ │ ├── t_option.txt
│ │ ├── tongue_option.txt
│ │ ├── wired_option.txt
│ │ └── y_option.txt
│ └── cowthink/
│ ├── W_option.txt
│ ├── b_option.txt
│ ├── d_option.txt
│ ├── eyes_option.txt
│ ├── f_tux_option.txt
│ ├── g_option.txt
│ ├── n_option.txt
│ ├── p_option.txt
│ ├── s_option.txt
│ ├── t_option.txt
│ ├── tongue_option.txt
│ ├── wired_option.txt
│ └── y_option.txt
├── cow.go
├── cow_test.go
├── cows/
│ ├── beavis.zen.cow
│ ├── bong.cow
│ ├── bud-frogs.cow
│ ├── bunny.cow
│ ├── cheese.cow
│ ├── cower.cow
│ ├── daemon.cow
│ ├── default.cow
│ ├── deno.cow
│ ├── docker.cow
│ ├── dragon-and-cow.cow
│ ├── dragon.cow
│ ├── elephant-in-snake.cow
│ ├── elephant.cow
│ ├── eyes.cow
│ ├── flaming-sheep.cow
│ ├── ghostbusters.cow
│ ├── gopher.cow
│ ├── head-in.cow
│ ├── hellokitty.cow
│ ├── kiss.cow
│ ├── kitty.cow
│ ├── koala.cow
│ ├── kosh.cow
│ ├── luke-koala.cow
│ ├── meow.cow
│ ├── milk.cow
│ ├── moofasa.cow
│ ├── moose.cow
│ ├── mutilated.cow
│ ├── ren.cow
│ ├── sage.cow
│ ├── satanic.cow
│ ├── sheep.cow
│ ├── skeleton.cow
│ ├── small.cow
│ ├── sodomized.cow
│ ├── squirrel.cow
│ ├── stegosaurus.cow
│ ├── stimpy.cow
│ ├── supermilker.cow
│ ├── surgery.cow
│ ├── telebears.cow
│ ├── turkey.cow
│ ├── turtle.cow
│ ├── tux.cow
│ ├── vader-koala.cow
│ ├── vader.cow
│ └── www.cow
├── cowsay.go
├── cowsay_test.go
├── decoration/
│ ├── aurora.go
│ ├── bold.go
│ ├── decoration.go
│ └── rainbow.go
├── doc/
│ ├── cowsay.1
│ └── cowsay.1.txt.tpl
├── embed.go
├── examples/
│ ├── basic/
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── main.go
│ └── echo-server/
│ ├── README.md
│ ├── go.mod
│ ├── go.sum
│ └── main.go
├── go.mod
├── go.sum
├── split.go
├── split_windows.go
└── testdata/
├── default.cow
├── nest.cow
└── testdir/
└── test.cow
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CODEOWNERS
================================================
* @Code-Hex
================================================
FILE: .github/FUNDING.yml
================================================
github: Code-Hex
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
================================================
FILE: .github/workflows/lgtm.yml
================================================
on:
pull_request_review:
types:
- submitted
jobs:
lgtm:
name: LGTM
runs-on: ubuntu-latest
if: github.event.review.state == 'approved'
steps:
- name: Cowsay LGTM
uses: Code-Hex/neo-cowsay-action@v1.0.2
with:
message: 'LGTM'
cow: 'random'
cowsay_on_comment: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/main.yml
================================================
on:
push:
branches:
- "master"
pull_request:
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
name: test
runs-on: ${{ matrix.os }}
steps:
- name: Setup Go 1.17.2
uses: actions/setup-go@v2
with:
go-version: 1.17.2
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Vet
run: make vet
- name: Test
run: make test
- name: Lint
if: matrix.os == 'ubuntu-latest'
run: |
go get golang.org/x/lint/golint
export PATH="$PATH:$(go env GOPATH)/bin"
make lint
env:
GO111MODULE: off
- name: Declare some variables
if: matrix.os == 'ubuntu-latest'
id: vars
run: |
echo "::set-output name=coverage_txt::${RUNNER_TEMP}/coverage.txt"
- name: Test Coverage (pkg)
if: matrix.os == 'ubuntu-latest'
run: go test ./... -coverprofile=${{ steps.vars.outputs.coverage_txt }}
- name: Upload coverage
if: matrix.os == 'ubuntu-latest'
uses: codecov/codecov-action@v3
with:
files: ${{ steps.vars.outputs.coverage_txt }}
================================================
FILE: .github/workflows/release.yml
================================================
on:
push:
tags:
- "v*.*.*"
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Setup Go 1.17.2
uses: actions/setup-go@v2
with:
go-version: 1.17.2
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Run GoReleaser
if: contains(github.ref, 'tags/v')
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
winget:
runs-on: ubuntu-latest
needs: release
steps:
- uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: codehex.Neo-cowsay
installers-regex: '_Windows_\w+\.zip$'
token: ${{ secrets.WINGET_TOKEN }}
================================================
FILE: .gitignore
================================================
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
github-token
build
releases
bin/*
!bin/.gitkeep
================================================
FILE: .goreleaser.yml
================================================
builds:
- id: cowsay
dir: cmd
main: ./cowsay/main.go
binary: cowsay
env:
- CGO_ENABLED=0
ldflags: -s -w -X main.version={{.Version}}
goos:
- linux
- darwin
- windows
goarch:
- 386
- amd64
- arm
- arm64
goarm:
- 6
- 7
ignore:
- goos: darwin
goarch: 386
- goos: linux
goarch: arm
goarm: 7
- goos: windows
goarch: arm
goarm: 7
- id: cowthink
dir: cmd
main: ./cowthink/main.go
binary: cowthink
env:
- CGO_ENABLED=0
ldflags: -s -w -X main.version={{.Version}}
goos:
- linux
- darwin
- windows
goarch:
- 386
- amd64
- arm
- arm64
goarm:
- 6
- 7
ignore:
- goos: darwin
goarch: 386
- goos: linux
goarch: arm
goarm: 7
- goos: windows
goarch: arm
goarm: 7
archives:
- builds:
- cowsay
- cowthink
name_template: 'cowsay_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
replacements:
darwin: macOS
linux: Linux
windows: Windows
386: i386
amd64: x86_64
format_overrides:
- goos: windows
format: zip
files:
- LICENSE
- doc/cowsay.1
brews:
- name: neo-cowsay
tap:
owner: Code-Hex
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
homepage: https://github.com/Code-Hex/Neo-cowsay
description: "Fast, funny, everyone wanted? new cowsay!!"
folder: Formula
install: |
bin.install "cowsay"
bin.install "cowthink"
man1.install Dir["doc/cowsay.1"]
nfpms:
- license: Artistic License 2.0
maintainer: Kei Kamikawa <x00.x7f.x86@gmail.com>
homepage: https://github.com/Code-Hex/Neo-cowsay
bindir: /usr/local/bin
description: "Fast, funny, everyone wanted? new cowsay!!"
formats:
- apk
- deb
- rpm
contents:
- src: "doc/cowsay.1"
dst: "/usr/share/man/man1/cowsay.1"
checksum:
name_template: 'cowsay_checksums.txt'
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- Merge pull request
- Merge branch
================================================
FILE: LICENSE
================================================
The Artistic License 2.0
Copyright (c) 2000-2006, The Perl Foundation.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
This license establishes the terms under which a given free software
Package may be copied, modified, distributed, and/or redistributed.
The intent is that the Copyright Holder maintains some artistic
control over the development of that Package while still keeping the
Package available as open source and free software.
You are always permitted to make arrangements wholly outside of this
license directly with the Copyright Holder of a given Package. If the
terms of this license do not permit the full use that you propose to
make of the Package, you should contact the Copyright Holder and seek
a different licensing arrangement.
Definitions
"Copyright Holder" means the individual(s) or organization(s)
named in the copyright notice for the entire Package.
"Contributor" means any party that has contributed code or other
material to the Package, in accordance with the Copyright Holder's
procedures.
"You" and "your" means any person who would like to copy,
distribute, or modify the Package.
"Package" means the collection of files distributed by the
Copyright Holder, and derivatives of that collection and/or of
those files. A given Package may consist of either the Standard
Version, or a Modified Version.
"Distribute" means providing a copy of the Package or making it
accessible to anyone else, or in the case of a company or
organization, to others outside of your company or organization.
"Distributor Fee" means any fee that you charge for Distributing
this Package or providing support for this Package to another
party. It does not mean licensing fees.
"Standard Version" refers to the Package if it has not been
modified, or has been modified only in ways explicitly requested
by the Copyright Holder.
"Modified Version" means the Package, if it has been changed, and
such changes were not explicitly requested by the Copyright
Holder.
"Original License" means this Artistic License as Distributed with
the Standard Version of the Package, in its current version or as
it may be modified by The Perl Foundation in the future.
"Source" form means the source code, documentation source, and
configuration files for the Package.
"Compiled" form means the compiled bytecode, object code, binary,
or any other form resulting from mechanical transformation or
translation of the Source form.
Permission for Use and Modification Without Distribution
(1) You are permitted to use the Standard Version and create and use
Modified Versions for any purpose without restriction, provided that
you do not Distribute the Modified Version.
Permissions for Redistribution of the Standard Version
(2) You may Distribute verbatim copies of the Source form of the
Standard Version of this Package in any medium without restriction,
either gratis or for a Distributor Fee, provided that you duplicate
all of the original copyright notices and associated disclaimers. At
your discretion, such verbatim copies may or may not include a
Compiled form of the Package.
(3) You may apply any bug fixes, portability changes, and other
modifications made available from the Copyright Holder. The resulting
Package will still be considered the Standard Version, and as such
will be subject to the Original License.
Distribution of Modified Versions of the Package as Source
(4) You may Distribute your Modified Version as Source (either gratis
or for a Distributor Fee, and with or without a Compiled form of the
Modified Version) provided that you clearly document how it differs
from the Standard Version, including, but not limited to, documenting
any non-standard features, executables, or modules, and provided that
you do at least ONE of the following:
(a) make the Modified Version available to the Copyright Holder
of the Standard Version, under the Original License, so that the
Copyright Holder may include your modifications in the Standard
Version.
(b) ensure that installation of your Modified Version does not
prevent the user installing or running the Standard Version. In
addition, the Modified Version must bear a name that is different
from the name of the Standard Version.
(c) allow anyone who receives a copy of the Modified Version to
make the Source form of the Modified Version available to others
under
(i) the Original License or
(ii) a license that permits the licensee to freely copy,
modify and redistribute the Modified Version using the same
licensing terms that apply to the copy that the licensee
received, and requires that the Source form of the Modified
Version, and of any works derived from it, be made freely
available in that license fees are prohibited but Distributor
Fees are allowed.
Distribution of Compiled Forms of the Standard Version
or Modified Versions without the Source
(5) You may Distribute Compiled forms of the Standard Version without
the Source, provided that you include complete instructions on how to
get the Source of the Standard Version. Such instructions must be
valid at the time of your distribution. If these instructions, at any
time while you are carrying out such distribution, become invalid, you
must provide new instructions on demand or cease further distribution.
If you provide valid instructions or cease distribution within thirty
days after you become aware that the instructions are invalid, then
you do not forfeit any of your rights under this license.
(6) You may Distribute a Modified Version in Compiled form without
the Source, provided that you comply with Section 4 with respect to
the Source of the Modified Version.
Aggregating or Linking the Package
(7) You may aggregate the Package (either the Standard Version or
Modified Version) with other packages and Distribute the resulting
aggregation provided that you do not charge a licensing fee for the
Package. Distributor Fees are permitted, and licensing fees for other
components in the aggregation are permitted. The terms of this license
apply to the use and Distribution of the Standard or Modified Versions
as included in the aggregation.
(8) You are permitted to link Modified and Standard Versions with
other works, to embed the Package in a larger work of your own, or to
build stand-alone binary or bytecode versions of applications that
include the Package, and Distribute the result without restriction,
provided the result does not expose a direct interface to the Package.
Items That are Not Considered Part of a Modified Version
(9) Works (including, but not limited to, modules and scripts) that
merely extend or make use of the Package, do not, by themselves, cause
the Package to be a Modified Version. In addition, such works are not
considered parts of the Package itself, and are not subject to the
terms of this license.
General Provisions
(10) Any use, modification, and distribution of the Standard or
Modified Versions is governed by this Artistic License. By using,
modifying or distributing the Package, you accept this license. Do not
use, modify, or distribute the Package, if you do not accept this
license.
(11) If your Modified Version has been derived from a Modified
Version made by someone other than you, you are nevertheless required
to ensure that your Modified Version complies with the requirements of
this license.
(12) This license does not grant you the right to use any trademark,
service mark, tradename, or logo of the Copyright Holder.
(13) This license includes the non-exclusive, worldwide,
free-of-charge patent license to make, have made, use, offer to sell,
sell, import and otherwise transfer the Package with respect to any
patent claims licensable by the Copyright Holder that are necessarily
infringed by the Package. If you institute patent litigation
(including a cross-claim or counterclaim) against any party alleging
that the Package constitutes direct or contributory patent
infringement, then this Artistic License to you shall terminate on the
date that such litigation is filed.
(14) Disclaimer of Warranty:
THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: Makefile
================================================
.PHONY: build
build: build/cowsay build/cowthink
.PHONY: build/cowsay
build/cowsay:
CGO_ENABLED=0 cd cmd && go build -o ../bin/cowsay -ldflags "-w -s" ./cowsay
.PHONY: build/cowthink
build/cowthink:
CGO_ENABLED=0 cd cmd && go build -o ../bin/cowthink -ldflags "-w -s" ./cowthink
.PHONY: lint
lint:
golint ./...
cd cmd && golint ./...
.PHONY: vet
vet:
go vet ./...
cd cmd && go vet ./...
.PHONY: test
test: test/pkg test/cli
.PHONY: test/pkg
test/pkg:
go test ./...
.PHONY: test/cli
test/cli:
cd cmd && go test ./...
.PHONY: man
man:
asciidoctor --doctype manpage --backend manpage doc/cowsay.1.txt.tpl -o doc/cowsay.1
.PHONY: man/preview
man/preview:
cat doc/cowsay.1 | groff -man -Tascii | less
================================================
FILE: README.md
================================================
# Neo Cowsay
Neo Cowsay is written in Go. This cowsay is extended the original cowsay. added fun more options, and you can be used as a library.
for GitHub Actions users: [Code-Hex/neo-cowsay-action](https://github.com/marketplace/actions/neo-cowsay)
[](https://pkg.go.dev/github.com/Code-Hex/Neo-cowsay/v2) [](https://github.com/Code-Hex/Neo-cowsay/actions/workflows/main.yml) [](https://goreportcard.com/report/github.com/Code-Hex/Neo-cowsay) [](https://codecov.io/gh/Code-Hex/Neo-cowsay)
```
______________
< I'm Neo cows >
--------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
## About cowsay
According to the [original](https://web.archive.org/web/20071026043648/http://www.nog.net/~tony/warez/cowsay.shtml) original manual.
```
cowsay is a configurable talking cow, written in Perl. It operates
much as the figlet program does, and it written in the same spirit
of silliness.
```
This is also supported `COWPATH` env. Please read more details in [#33](https://github.com/Code-Hex/Neo-cowsay/pull/33) if you want to use this.
## What makes it different from the original?
- fast
- utf8 is supported
- new some cowfiles is added
- cowfiles in binary
- random pickup cowfile option
- provides command-line fuzzy finder to search any cows with `-f -` [#39](https://github.com/Code-Hex/Neo-cowsay/pull/39)
- coloring filter options
- super mode
<details>
<summary>Movies for new options 🐮</summary>
### Random
[](https://asciinema.org/a/228210)
### Rainbow and Aurora, Bold
[](https://asciinema.org/a/228213)
## And, Super Cows mode
https://user-images.githubusercontent.com/6500104/140379043-53e44994-b1b0-442e-bda7-4f7ab3aedf01.mov
</details>
## Usage
### As command
```
cow{say,think} version 2.0.0, (c) 2021 codehex
Usage: cowsay [-bdgpstwy] [-h] [-e eyes] [-f cowfile] [--random]
[-l] [-n] [-T tongue] [-W wrapcolumn]
[--bold] [--rainbow] [--aurora] [--super] [message]
Original Author: (c) 1999 Tony Monroe
Repository: https://github.com/Code-Hex/Neo-cowsay
```
Normal
```
$ cowsay Hello
_______
< Hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
Borg mode
```
$ cowsay -b Hello
_______
< Hello >
-------
\ ^__^
\ (==)\_______
(__)\ )\/\
||----w |
|| ||
```
### As library
```go
package main
import (
"fmt"
cowsay "github.com/Code-Hex/Neo-cowsay/v2"
)
func main() {
say, err := cowsay.Say(
"Hello",
cowsay.Type("default"),
cowsay.BallonWidth(40),
)
if err != nil {
panic(err)
}
fmt.Println(say)
}
```
[Examples](https://github.com/Code-Hex/Neo-cowsay/blob/master/examples) or [GoDoc](https://pkg.go.dev/github.com/Code-Hex/Neo-cowsay/v2)
## Install
### Windows users via Scoop
$ scoop install neo-cowsay
### Windows users via Winget
$ winget install neo-cowsay
### Mac and Linux users via Homebrew
$ brew update
$ brew install Code-Hex/tap/neo-cowsay
### Binary
You can download from [here](https://github.com/Code-Hex/Neo-cowsay/releases)
### library
$ go get github.com/Code-Hex/Neo-cowsay/v2
### Go
#### cowsay
$ go install github.com/Code-Hex/Neo-cowsay/cmd/v2/cowsay@latest
#### cowthink
$ go install github.com/Code-Hex/Neo-cowsay/cmd/v2/cowthink@latest
## License
<details>
<summary>cowsay license</summary>
```
==============
cowsay License
==============
cowsay is distributed under the same licensing terms as Perl: the
Artistic License or the GNU General Public License. If you don't
want to track down these licenses and read them for yourself, use
the parts that I'd prefer:
(0) I wrote it and you didn't.
(1) Give credit where credit is due if you borrow the code for some
other purpose.
(2) If you have any bugfixes or suggestions, please notify me so
that I may incorporate them.
(3) If you try to make money off of cowsay, you suck.
===============
cowsay Legalese
===============
(0) Copyright (c) 1999 Tony Monroe. All rights reserved. All
lefts may or may not be reversed at my discretion.
(1) This software package can be freely redistributed or modified
under the terms described above in the "cowsay License" section
of this file.
(2) cowsay is provided "as is," with no warranties whatsoever,
expressed or implied. If you want some implied warranty about
merchantability and/or fitness for a particular purpose, you will
not find it here, because there is no such thing here.
(3) I hate legalese.
```
</details>
(The Artistic License or The GNU General Public License)
## Author
Neo Cowsay: [codehex](https://twitter.com/CodeHex)
Original: (c) 1999 Tony Monroe
================================================
FILE: balloon.go
================================================
package cowsay
import (
"fmt"
"strings"
wordwrap "github.com/Code-Hex/go-wordwrap"
runewidth "github.com/mattn/go-runewidth"
)
type border struct {
first [2]rune
middle [2]rune
last [2]rune
only [2]rune
}
func (cow *Cow) borderType() border {
if cow.thinking {
return border{
first: [2]rune{'(', ')'},
middle: [2]rune{'(', ')'},
last: [2]rune{'(', ')'},
only: [2]rune{'(', ')'},
}
}
return border{
first: [2]rune{'/', '\\'},
middle: [2]rune{'|', '|'},
last: [2]rune{'\\', '/'},
only: [2]rune{'<', '>'},
}
}
type line struct {
text string
runeWidth int
}
type lines []*line
func (cow *Cow) maxLineWidth(lines []*line) int {
maxWidth := 0
for _, line := range lines {
if line.runeWidth > maxWidth {
maxWidth = line.runeWidth
}
if !cow.disableWordWrap && maxWidth > cow.ballonWidth {
return cow.ballonWidth
}
}
return maxWidth
}
func (cow *Cow) getLines(phrase string) []*line {
text := cow.canonicalizePhrase(phrase)
lineTexts := strings.Split(text, "\n")
lines := make([]*line, 0, len(lineTexts))
for _, lineText := range lineTexts {
lines = append(lines, &line{
text: lineText,
runeWidth: runewidth.StringWidth(lineText),
})
}
return lines
}
func (cow *Cow) canonicalizePhrase(phrase string) string {
// Replace tab to 8 spaces
phrase = strings.Replace(phrase, "\t", " ", -1)
if cow.disableWordWrap {
return phrase
}
width := cow.ballonWidth
return wordwrap.WrapString(phrase, uint(width))
}
// Balloon to get the balloon and the string entered in the balloon.
func (cow *Cow) Balloon(phrase string) string {
defer cow.buf.Reset()
lines := cow.getLines(phrase)
maxWidth := cow.maxLineWidth(lines)
cow.writeBallon(lines, maxWidth)
return cow.buf.String()
}
func (cow *Cow) writeBallon(lines []*line, maxWidth int) {
top := make([]byte, 0, maxWidth+2)
bottom := make([]byte, 0, maxWidth+2)
top = append(top, ' ')
bottom = append(bottom, ' ')
for i := 0; i < maxWidth+2; i++ {
top = append(top, '_')
bottom = append(bottom, '-')
}
borderType := cow.borderType()
cow.buf.Write(top)
cow.buf.Write([]byte{' ', '\n'})
defer func() {
cow.buf.Write(bottom)
cow.buf.Write([]byte{' ', '\n'})
}()
l := len(lines)
if l == 1 {
border := borderType.only
cow.buf.WriteRune(border[0])
cow.buf.WriteRune(' ')
cow.buf.WriteString(lines[0].text)
cow.buf.WriteRune(' ')
cow.buf.WriteRune(border[1])
cow.buf.WriteRune('\n')
return
}
var border [2]rune
for i := 0; i < l; i++ {
switch i {
case 0:
border = borderType.first
case l - 1:
border = borderType.last
default:
border = borderType.middle
}
cow.buf.WriteRune(border[0])
cow.buf.WriteRune(' ')
cow.padding(lines[i], maxWidth)
cow.buf.WriteRune(' ')
cow.buf.WriteRune(border[1])
cow.buf.WriteRune('\n')
}
}
func (cow *Cow) flush(text, top, bottom fmt.Stringer) string {
return fmt.Sprintf(
"%s\n%s%s\n",
top.String(),
text.String(),
bottom.String(),
)
}
func (cow *Cow) padding(line *line, maxWidth int) {
if maxWidth <= line.runeWidth {
cow.buf.WriteString(line.text)
return
}
cow.buf.WriteString(line.text)
l := maxWidth - line.runeWidth
for i := 0; i < l; i++ {
cow.buf.WriteRune(' ')
}
}
================================================
FILE: bin/.gitkeep
================================================
================================================
FILE: cmd/cmd.go
================================================
package cmd
================================================
FILE: cmd/cowsay/main.go
================================================
package main
import (
"os"
"github.com/Code-Hex/Neo-cowsay/cmd/v2/internal/cli"
)
var version string
func main() {
os.Exit((&cli.CLI{
Version: version,
Thinking: false,
}).Run(os.Args[1:]))
}
================================================
FILE: cmd/cowthink/main.go
================================================
package main
import (
"os"
"github.com/Code-Hex/Neo-cowsay/cmd/v2/internal/cli"
)
var version string
func main() {
os.Exit((&cli.CLI{
Version: version,
Thinking: true,
}).Run(os.Args[1:]))
}
================================================
FILE: cmd/go.mod
================================================
module github.com/Code-Hex/Neo-cowsay/cmd/v2
go 1.16
require (
github.com/Code-Hex/Neo-cowsay/v2 v2.0.3
github.com/Code-Hex/go-wordwrap v1.0.0
github.com/google/go-cmp v0.5.6
github.com/jessevdk/go-flags v1.5.0
github.com/ktr0731/go-fuzzyfinder v0.5.1
github.com/mattn/go-colorable v0.1.11
github.com/mattn/go-runewidth v0.0.13
github.com/rivo/uniseg v0.2.0
golang.org/x/crypto v0.1.0
)
replace github.com/Code-Hex/Neo-cowsay/v2 => ../
================================================
FILE: cmd/go.sum
================================================
github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=
github.com/Code-Hex/go-wordwrap v1.0.0/go.mod h1:/SsbgkY2Q0aPQRyvXcyQwWYTQOIwSORKe6MPjRVGIWU=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM=
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/ktr0731/go-fuzzyfinder v0.5.1 h1:rDcWxmGi6ux4NURekn9iAXpbYBp8Kj4cznrz162S9og=
github.com/ktr0731/go-fuzzyfinder v0.5.1/go.mod h1:gud27uRG2vF+oD58eGhYZj7Pc9enRX0qecwp09w/jno=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 h1:Rl8NelBe+n7SuLbJyw13ho7CGWUt2BjGGKIoreCWQ/c=
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
================================================
FILE: cmd/internal/cli/cli.go
================================================
package cli
import (
"bufio"
cryptorand "crypto/rand"
"errors"
"fmt"
"io"
"math"
"math/big"
"math/rand"
"os"
"strconv"
"strings"
"time"
"github.com/Code-Hex/Neo-cowsay/cmd/v2/internal/super"
cowsay "github.com/Code-Hex/Neo-cowsay/v2"
"github.com/Code-Hex/Neo-cowsay/v2/decoration"
"github.com/Code-Hex/go-wordwrap"
"github.com/jessevdk/go-flags"
"github.com/ktr0731/go-fuzzyfinder"
"github.com/mattn/go-colorable"
)
func init() {
// safely set the seed globally so we generate random ids. Tries to use a
// crypto seed before falling back to time.
var seed int64
cryptoseed, err := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
// This should not happen, but worst-case fallback to time-based seed.
seed = time.Now().UnixNano()
} else {
seed = cryptoseed.Int64()
}
rand.Seed(seed)
}
// options struct for parse command line arguments
type options struct {
Help bool `short:"h"`
Eyes string `short:"e"`
Tongue string `short:"T"`
Width int `short:"W"`
Borg bool `short:"b"`
Dead bool `short:"d"`
Greedy bool `short:"g"`
Paranoia bool `short:"p"`
Stoned bool `short:"s"`
Tired bool `short:"t"`
Wired bool `short:"w"`
Youthful bool `short:"y"`
List bool `short:"l"`
NewLine bool `short:"n"`
File string `short:"f"`
Bold bool `long:"bold"`
Super bool `long:"super"`
Random bool `long:"random"`
Rainbow bool `long:"rainbow"`
Aurora bool `long:"aurora"`
}
// CLI prepare for running command-line.
type CLI struct {
Version string
Thinking bool
stderr io.Writer
stdout io.Writer
stdin io.Reader
}
func (c *CLI) program() string {
if c.Thinking {
return "cowthink"
}
return "cowsay"
}
// Run runs command-line.
func (c *CLI) Run(argv []string) int {
if c.stderr == nil {
c.stderr = os.Stderr
}
if c.stdout == nil {
c.stdout = colorable.NewColorableStdout()
}
if c.stdin == nil {
c.stdin = os.Stdin
}
if err := c.mow(argv); err != nil {
fmt.Fprintf(c.stderr, "%s: %s\n", c.program(), err.Error())
return 1
}
return 0
}
// mow will parsing for cowsay command line arguments and invoke cowsay.
func (c *CLI) mow(argv []string) error {
var opts options
args, err := c.parseOptions(&opts, argv)
if err != nil {
return err
}
if opts.List {
cowPaths, err := cowsay.Cows()
if err != nil {
return err
}
for _, cowPath := range cowPaths {
if cowPath.LocationType == cowsay.InBinary {
fmt.Fprintf(c.stdout, "Cow files in binary:\n")
} else {
fmt.Fprintf(c.stdout, "Cow files in %s:\n", cowPath.Name)
}
fmt.Fprintln(c.stdout, wordwrap.WrapString(strings.Join(cowPath.CowFiles, " "), 80))
fmt.Fprintln(c.stdout)
}
return nil
}
if err := c.mowmow(&opts, args); err != nil {
return err
}
return nil
}
func (c *CLI) parseOptions(opts *options, argv []string) ([]string, error) {
p := flags.NewParser(opts, flags.None)
args, err := p.ParseArgs(argv)
if err != nil {
return nil, err
}
if opts.Help {
c.stdout.Write(c.usage())
os.Exit(0)
}
return args, nil
}
func (c *CLI) usage() []byte {
year := strconv.Itoa(time.Now().Year())
return []byte(c.program() + ` version ` + c.Version + `, (c) ` + year + ` codehex
Usage: ` + c.program() + ` [-bdgpstwy] [-h] [-e eyes] [-f cowfile] [--random]
[-l] [-n] [-T tongue] [-W wrapcolumn]
[--bold] [--rainbow] [--aurora] [--super] [message]
Original Author: (c) 1999 Tony Monroe
`)
}
func (c *CLI) generateOptions(opts *options) []cowsay.Option {
o := make([]cowsay.Option, 0, 8)
if opts.File == "-" {
cows := cowList()
idx, _ := fuzzyfinder.Find(cows, func(i int) string {
return cows[i]
})
opts.File = cows[idx]
}
o = append(o, cowsay.Type(opts.File))
if c.Thinking {
o = append(o,
cowsay.Thinking(),
cowsay.Thoughts('o'),
)
}
if opts.Random {
o = append(o, cowsay.Random())
}
if opts.Eyes != "" {
o = append(o, cowsay.Eyes(opts.Eyes))
}
if opts.Tongue != "" {
o = append(o, cowsay.Tongue(opts.Tongue))
}
if opts.Width > 0 {
o = append(o, cowsay.BallonWidth(uint(opts.Width)))
}
if opts.NewLine {
o = append(o, cowsay.DisableWordWrap())
}
return selectFace(opts, o)
}
func cowList() []string {
cows, err := cowsay.Cows()
if err != nil {
return cowsay.CowsInBinary()
}
list := make([]string, 0)
for _, cow := range cows {
list = append(list, cow.CowFiles...)
}
return list
}
func (c *CLI) phrase(opts *options, args []string) string {
if len(args) > 0 {
return strings.Join(args, " ")
}
lines := make([]string, 0, 40)
scanner := bufio.NewScanner(c.stdin)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return strings.Join(lines, "\n")
}
func (c *CLI) mowmow(opts *options, args []string) error {
phrase := c.phrase(opts, args)
o := c.generateOptions(opts)
if opts.Super {
return super.RunSuperCow(phrase, opts.Bold, o...)
}
say, err := cowsay.Say(phrase, o...)
if err != nil {
var notfound *cowsay.NotFound
if errors.As(err, ¬found) {
return fmt.Errorf("could not find %s cowfile", notfound.Cowfile)
}
return err
}
options := make([]decoration.Option, 0)
if opts.Bold {
options = append(options, decoration.WithBold())
}
if opts.Rainbow {
options = append(options, decoration.WithRainbow())
}
if opts.Aurora {
options = append(options, decoration.WithAurora(rand.Intn(256)))
}
w := decoration.NewWriter(c.stdout, options...)
fmt.Fprintln(w, say)
return nil
}
func selectFace(opts *options, o []cowsay.Option) []cowsay.Option {
switch {
case opts.Borg:
o = append(o,
cowsay.Eyes("=="),
cowsay.Tongue(" "),
)
case opts.Dead:
o = append(o,
cowsay.Eyes("xx"),
cowsay.Tongue("U "),
)
case opts.Greedy:
o = append(o,
cowsay.Eyes("$$"),
cowsay.Tongue(" "),
)
case opts.Paranoia:
o = append(o,
cowsay.Eyes("@@"),
cowsay.Tongue(" "),
)
case opts.Stoned:
o = append(o,
cowsay.Eyes("**"),
cowsay.Tongue("U "),
)
case opts.Tired:
o = append(o,
cowsay.Eyes("--"),
cowsay.Tongue(" "),
)
case opts.Wired:
o = append(o,
cowsay.Eyes("OO"),
cowsay.Tongue(" "),
)
case opts.Youthful:
o = append(o,
cowsay.Eyes(".."),
cowsay.Tongue(" "),
)
}
return o
}
================================================
FILE: cmd/internal/cli/cli_test.go
================================================
package cli
import (
"bytes"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
)
func TestCLI_Run(t *testing.T) {
clis := []struct {
name string
thinking bool
}{
{
name: "cowsay",
thinking: false,
},
{
name: "cowthink",
thinking: true,
},
}
for _, cli := range clis {
cli := cli
t.Run(cli.name, func(t *testing.T) {
t.Parallel()
tests := []struct {
name string
phrase string
argv []string
testfile string
}{
{
name: "ignore wordwrap option",
phrase: "foo\nbar\nbaz",
argv: []string{"-n"},
testfile: "n_option.txt",
},
{
name: "tired option",
phrase: "tired",
argv: []string{"-t"},
testfile: "t_option.txt",
},
{
name: "specifies width of the ballon is 3",
phrase: "foobarbaz",
argv: []string{"-W", "3"},
testfile: "W_option.txt",
},
{
name: "borg mode",
phrase: "foobarbaz",
argv: []string{"-b"},
testfile: "b_option.txt",
},
{
name: "dead mode",
phrase: "0xdeadbeef",
argv: []string{"-d"},
testfile: "d_option.txt",
},
{
name: "greedy mode",
phrase: "give me money",
argv: []string{"-g"},
testfile: "g_option.txt",
},
{
name: "paranoid mode",
phrase: "everyone hates me",
argv: []string{"-p"},
testfile: "p_option.txt",
},
{
name: "stoned mode",
phrase: "I don't know",
argv: []string{"-s"},
testfile: "s_option.txt",
},
{
name: "wired mode",
phrase: "Wanna Netflix and chill?",
argv: []string{"-w"},
testfile: "wired_option.txt",
},
{
name: "youthful mode",
phrase: "I forgot my ID at home",
argv: []string{"-y"},
testfile: "y_option.txt",
},
{
name: "eyes option",
phrase: "I'm not angry",
argv: []string{"-e", "^^"},
testfile: "eyes_option.txt",
},
{
name: "tongue option",
phrase: "hungry",
argv: []string{"-T", ":"},
testfile: "tongue_option.txt",
},
{
name: "-f tux",
phrase: "what is macOS?",
argv: []string{"-f", "tux"},
testfile: "f_tux_option.txt",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var stdout bytes.Buffer
c := &CLI{
Thinking: cli.thinking,
stdout: &stdout,
stdin: strings.NewReader(tt.phrase),
}
exit := c.Run(tt.argv)
if exit != 0 {
t.Fatalf("unexpected exit code: %d", exit)
}
testpath := filepath.Join("..", "..", "testdata", cli.name, tt.testfile)
content, err := ioutil.ReadFile(testpath)
if err != nil {
t.Fatal(err)
}
got := strings.Replace(stdout.String(), "\r", "", -1) // for windows
want := strings.Replace(string(content), "\r", "", -1) // for windows
if want != got {
t.Log(cmp.Diff(want, got))
t.Errorf("want\n%s\n-----got\n%s\n", want, got)
}
})
}
t.Run("program name", func(t *testing.T) {
c := &CLI{Thinking: cli.thinking}
if cli.name != c.program() {
t.Fatalf("want %q, but got %q", cli.name, c.program())
}
})
t.Run("not found cowfile", func(t *testing.T) {
var stderr bytes.Buffer
c := &CLI{
Thinking: cli.thinking,
stderr: &stderr,
}
exit := c.Run([]string{"-f", "unknown"})
if exit == 0 {
t.Errorf("unexpected exit code: %d", exit)
}
want := fmt.Sprintf("%s: could not find unknown cowfile\n", cli.name)
if want != stderr.String() {
t.Errorf("want %q, but got %q", want, stderr.String())
}
})
})
}
}
================================================
FILE: cmd/internal/screen/buffer.go
================================================
package screen
import (
"bytes"
"fmt"
"io"
"strings"
)
// buffer is the global screen buffer
// Its not recommended write to buffer dirrectly, use package Print,Printf,Println functions instead.
var buffer strings.Builder
// Flush buffer and ensure that it will not overflow screen
func Flush() string {
defer buffer.Reset()
return buffer.String()
}
// MoveWriter is implemented io.Writer and io.StringWriter.
type MoveWriter struct {
idx int
x, y int
w io.Writer
buf bytes.Buffer
}
var _ interface {
io.Writer
io.StringWriter
} = (*MoveWriter)(nil)
// NewMoveWriter creates a new MoveWriter.
func NewMoveWriter(w io.Writer, x, y int) *MoveWriter {
x, y = getXY(x, y)
return &MoveWriter{
w: w,
x: x,
y: y,
}
}
// SetPosx sets pos x
func (m *MoveWriter) SetPosx(x int) {
x, _ = getXY(x, 0)
m.x = x
}
// Reset resets
func (m *MoveWriter) Reset() {
m.idx = 0
m.buf.Reset()
}
// Write writes bytes. which is implemented io.Writer.
func (m *MoveWriter) Write(bs []byte) (nn int, _ error) {
br := bytes.NewReader(bs)
for {
b, err := br.ReadByte()
if err != nil && err != io.EOF {
return 0, err
}
if err == io.EOF {
n, _ := fmt.Fprintf(m.w, "\x1b[%d;%dH%s\x1b[0K",
m.y+m.idx,
m.x,
m.buf.String(),
)
nn += n
return
}
if b == '\n' {
n, _ := fmt.Fprintf(m.w, "\x1b[%d;%dH%s\x1b[0K",
m.y+m.idx,
m.x,
m.buf.String(),
)
m.buf.Reset()
m.idx++
nn += n
} else {
m.buf.WriteByte(b)
}
}
}
// WriteString writes string. which is implemented io.StringWriter.
func (m *MoveWriter) WriteString(s string) (nn int, _ error) {
for _, char := range s {
if char == '\n' {
n, _ := fmt.Fprintf(m.w, "\x1b[%d;%dH%s\x1b[0K",
m.y+m.idx,
m.x,
m.buf.String(),
)
m.buf.Reset()
m.idx++
nn += n
} else {
m.buf.WriteRune(char)
}
}
if m.buf.Len() > 0 {
n, _ := fmt.Fprintf(m.w, "\x1b[%d;%dH%s\x1b[0K",
m.y+m.idx,
m.x,
m.buf.String(),
)
nn += n
}
return
}
// getXY gets relative or absolute coorditantes
// To get relative, set PCT flag to number:
//
// // Get 10% of total width to `x` and 20 to y
// x, y = tm.GetXY(10|tm.PCT, 20)
//
func getXY(x int, y int) (int, int) {
// Set percent flag: num | PCT
//
// Check percent flag: num & PCT
//
// Reset percent flag: num & 0xFF
const shift = uint(^uint(0)>>63) << 4
const PCT = 0x8000 << shift
if y == -1 {
y = currentHeight() + 1
}
if x&PCT != 0 {
x = int((x & 0xFF) * Width() / 100)
}
if y&PCT != 0 {
y = int((y & 0xFF) * Height() / 100)
}
return x, y
}
// currentHeight returns current height. Line count in Screen buffer.
func currentHeight() int {
return strings.Count(buffer.String(), "\n")
}
================================================
FILE: cmd/internal/screen/screen.go
================================================
package screen
import (
"os"
"sync"
colorable "github.com/mattn/go-colorable"
"golang.org/x/crypto/ssh/terminal"
)
// Stdout color supported stdout
var Stdout = colorable.NewColorableStdout()
// SaveState saves cursor state.
func SaveState() { Stdout.Write([]byte("\0337")) }
// RestoreState restores cursor state.
func RestoreState() { Stdout.Write([]byte("\0338")) }
// Clear clears terminal screen.
func Clear() { Stdout.Write([]byte("\033[2J")) }
// HideCursor hide the cursor
func HideCursor() { Stdout.Write([]byte("\033[?25l")) }
// UnHideCursor unhide the cursor
func UnHideCursor() { Stdout.Write([]byte("\033[?25h")) }
var size struct {
once sync.Once
width int
height int
}
func getSize() (int, int) {
size.once.Do(func() {
var err error
size.width, size.height, err = terminal.GetSize(int(os.Stdout.Fd()))
if err != nil {
size.width, size.height = -1, -1
}
})
return size.width, size.height
}
// Width returns console width
func Width() int {
width, _ := getSize()
return width
}
// Height returns console height
func Height() int {
_, height := getSize()
return height
}
================================================
FILE: cmd/internal/super/supercow.go
================================================
package super
import (
"errors"
"io"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/Code-Hex/Neo-cowsay/cmd/v2/internal/screen"
cowsay "github.com/Code-Hex/Neo-cowsay/v2"
"github.com/Code-Hex/Neo-cowsay/v2/decoration"
runewidth "github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
)
func getNoSaidCow(cow *cowsay.Cow, opts ...cowsay.Option) (string, error) {
opts = append(opts, cowsay.Thoughts(' '))
cow, err := cow.Clone(opts...)
if err != nil {
return "", err
}
return cow.GetCow()
}
// RunSuperCow runs super cow mode animation on the your terminal
func RunSuperCow(phrase string, withBold bool, opts ...cowsay.Option) error {
cow, err := cowsay.New(opts...)
if err != nil {
return err
}
balloon := cow.Balloon(phrase)
blank := createBlankSpace(balloon)
said, err := cow.GetCow()
if err != nil {
return err
}
notSaid, err := getNoSaidCow(cow, opts...)
if err != nil {
return err
}
saidCow := balloon + said
saidCowLines := strings.Count(saidCow, "\n") + 1
// When it is higher than the height of the terminal
h := screen.Height()
if saidCowLines > h {
return errors.New("too height messages")
}
notSaidCow := blank + notSaid
renderer := newRenderer(saidCow, notSaidCow)
screen.SaveState()
screen.HideCursor()
screen.Clear()
go renderer.createFrames(cow, withBold)
renderer.render()
screen.UnHideCursor()
screen.RestoreState()
return nil
}
func createBlankSpace(balloon string) string {
var buf strings.Builder
l := strings.Count(balloon, "\n")
for i := 0; i < l; i++ {
buf.WriteRune('\n')
}
return buf.String()
}
func maxLen(cow []string) int {
max := 0
for _, line := range cow {
l := runewidth.StringWidth(line)
if max < l {
max = l
}
}
return max
}
type cowLine struct {
raw string
clusters []rune
}
func (c *cowLine) Len() int {
return len(c.clusters)
}
func (c *cowLine) Slice(i, j int) string {
if c.Len() == 0 {
return ""
}
return string(c.clusters[i:j])
}
func makeCowLines(cow string) []*cowLine {
sep := strings.Split(cow, "\n")
cowLines := make([]*cowLine, len(sep))
for i, line := range sep {
g := uniseg.NewGraphemes(line)
clusters := make([]rune, 0)
for g.Next() {
clusters = append(clusters, g.Runes()...)
}
cowLines[i] = &cowLine{
raw: line,
clusters: clusters,
}
}
return cowLines
}
type renderer struct {
max int
middle int
screenWidth int
heightDiff int
frames chan string
saidCow string
notSaidCowLines []*cowLine
quit chan os.Signal
}
func newRenderer(saidCow, notSaidCow string) *renderer {
notSaidCowSep := strings.Split(notSaidCow, "\n")
w, cowsWidth := screen.Width(), maxLen(notSaidCowSep)
max := w + cowsWidth
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
return &renderer{
max: max,
middle: max / 2,
screenWidth: w,
heightDiff: screen.Height() - strings.Count(saidCow, "\n") - 1,
frames: make(chan string, max),
saidCow: saidCow,
notSaidCowLines: makeCowLines(notSaidCow),
quit: quit,
}
}
const (
// Frequency the color changes
magic = 2
span = 30 * time.Millisecond
standup = 3 * time.Second
)
func (r *renderer) createFrames(cow *cowsay.Cow, withBold bool) {
const times = standup / span
w := r.newWriter(withBold)
for x, i := 0, 1; i <= r.max; i++ {
if i == r.middle {
w.SetPosx(r.posX(i))
for k := 0; k < int(times); k++ {
base := x * 70
// draw colored cow
w.SetColorSeq(base)
w.WriteString(r.saidCow)
r.frames <- w.String()
if k%magic == 0 {
x++
}
}
} else {
base := x * 70
w.SetPosx(r.posX(i))
w.SetColorSeq(base)
for _, line := range r.notSaidCowLines {
if i > r.screenWidth {
// Left side animations
n := i - r.screenWidth
if n < line.Len() {
w.WriteString(line.Slice(n, line.Len()))
}
} else if i <= line.Len() {
// Right side animations
w.WriteString(line.Slice(0, i-1))
} else {
w.WriteString(line.raw)
}
w.Write([]byte{'\n'})
}
r.frames <- w.String()
}
if i%magic == 0 {
x++
}
}
close(r.frames)
}
func (r *renderer) render() {
initCh := make(chan struct{}, 1)
initCh <- struct{}{}
for view := range r.frames {
select {
case <-r.quit:
screen.Clear()
return
case <-initCh:
case <-time.After(span):
}
io.Copy(screen.Stdout, strings.NewReader(view))
}
}
func (r *renderer) posX(i int) int {
posx := r.screenWidth - i
if posx < 1 {
posx = 1
}
return posx
}
// Writer is wrapper which is both screen.MoveWriter and decoration.Writer.
type Writer struct {
buf *strings.Builder
mw *screen.MoveWriter
dw *decoration.Writer
}
func (r *renderer) newWriter(withBold bool) *Writer {
var buf strings.Builder
mw := screen.NewMoveWriter(&buf, r.posX(0), r.heightDiff)
options := []decoration.Option{
decoration.WithAurora(0),
}
if withBold {
options = append(options, decoration.WithBold())
}
dw := decoration.NewWriter(mw, options...)
return &Writer{
buf: &buf,
mw: mw,
dw: dw,
}
}
// WriteString writes string. which is implemented io.StringWriter.
func (w *Writer) WriteString(s string) (int, error) { return w.dw.WriteString(s) }
// Write writes bytes. which is implemented io.Writer.
func (w *Writer) Write(p []byte) (int, error) { return w.dw.Write(p) }
// SetPosx sets posx
func (w *Writer) SetPosx(x int) { w.mw.SetPosx(x) }
// SetColorSeq sets color sequence.
func (w *Writer) SetColorSeq(colorSeq int) { w.dw.SetColorSeq(colorSeq) }
// Reset resets calls some Reset methods.
func (w *Writer) Reset() {
w.buf.Reset()
w.mw.Reset()
}
func (w *Writer) String() string {
defer w.Reset()
return w.buf.String()
}
================================================
FILE: cmd/testdata/cowsay/W_option.txt
================================================
_____
/ foo \
| bar |
\ baz /
-----
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/b_option.txt
================================================
___________
< foobarbaz >
-----------
\ ^__^
\ (==)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/d_option.txt
================================================
____________
< 0xdeadbeef >
------------
\ ^__^
\ (xx)\_______
(__)\ )\/\
U ||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/eyes_option.txt
================================================
_______________
< I'm not angry >
---------------
\ ^__^
\ (^^)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/f_tux_option.txt
================================================
________________
< what is macOS? >
----------------
\
\
.--.
|o_o |
|:_/ |
// \ \
(| | )
/'\_ _/`\
\___)=(___/
================================================
FILE: cmd/testdata/cowsay/g_option.txt
================================================
_______________
< give me money >
---------------
\ ^__^
\ ($$)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/n_option.txt
================================================
_____
/ foo \
| bar |
\ baz /
-----
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/p_option.txt
================================================
___________________
< everyone hates me >
-------------------
\ ^__^
\ (@@)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/s_option.txt
================================================
______________
< I don't know >
--------------
\ ^__^
\ (**)\_______
(__)\ )\/\
U ||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/t_option.txt
================================================
_______
< tired >
-------
\ ^__^
\ (--)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/tongue_option.txt
================================================
________
< hungry >
--------
\ ^__^
\ (oo)\_______
(__)\ )\/\
: ||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/wired_option.txt
================================================
__________________________
< Wanna Netflix and chill? >
--------------------------
\ ^__^
\ (OO)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowsay/y_option.txt
================================================
________________________
< I forgot my ID at home >
------------------------
\ ^__^
\ (..)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/W_option.txt
================================================
_____
( foo )
( bar )
( baz )
-----
o ^__^
o (oo)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/b_option.txt
================================================
___________
( foobarbaz )
-----------
o ^__^
o (==)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/d_option.txt
================================================
____________
( 0xdeadbeef )
------------
o ^__^
o (xx)\_______
(__)\ )\/\
U ||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/eyes_option.txt
================================================
_______________
( I'm not angry )
---------------
o ^__^
o (^^)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/f_tux_option.txt
================================================
________________
( what is macOS? )
----------------
o
o
.--.
|o_o |
|:_/ |
// \ \
(| | )
/'\_ _/`\
\___)=(___/
================================================
FILE: cmd/testdata/cowthink/g_option.txt
================================================
_______________
( give me money )
---------------
o ^__^
o ($$)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/n_option.txt
================================================
_____
( foo )
( bar )
( baz )
-----
o ^__^
o (oo)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/p_option.txt
================================================
___________________
( everyone hates me )
-------------------
o ^__^
o (@@)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/s_option.txt
================================================
______________
( I don't know )
--------------
o ^__^
o (**)\_______
(__)\ )\/\
U ||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/t_option.txt
================================================
_______
( tired )
-------
o ^__^
o (--)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/tongue_option.txt
================================================
________
( hungry )
--------
o ^__^
o (oo)\_______
(__)\ )\/\
: ||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/wired_option.txt
================================================
__________________________
( Wanna Netflix and chill? )
--------------------------
o ^__^
o (OO)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cmd/testdata/cowthink/y_option.txt
================================================
________________________
( I forgot my ID at home )
------------------------
o ^__^
o (..)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: cow.go
================================================
package cowsay
import (
"fmt"
"math/rand"
"strings"
)
// Cow struct!!
type Cow struct {
eyes string
tongue string
typ *CowFile
thoughts rune
thinking bool
ballonWidth int
disableWordWrap bool
buf strings.Builder
}
// New returns pointer of Cow struct that made by options
func New(options ...Option) (*Cow, error) {
cow := &Cow{
eyes: "oo",
tongue: " ",
thoughts: '\\',
typ: &CowFile{
Name: "default",
BasePath: "cows",
LocationType: InBinary,
},
ballonWidth: 40,
}
for _, o := range options {
if err := o(cow); err != nil {
return nil, err
}
}
return cow, nil
}
// Say returns string that said by cow
func (cow *Cow) Say(phrase string) (string, error) {
mow, err := cow.GetCow()
if err != nil {
return "", err
}
return cow.Balloon(phrase) + mow, nil
}
// Clone returns a copy of cow.
//
// If any options are specified, they will be reflected.
func (cow *Cow) Clone(options ...Option) (*Cow, error) {
ret := new(Cow)
*ret = *cow
ret.buf.Reset()
for _, o := range options {
if err := o(ret); err != nil {
return nil, err
}
}
return ret, nil
}
// Option defined for Options
type Option func(*Cow) error
// Eyes specifies eyes
// The specified string will always be adjusted to be equal to two characters.
func Eyes(s string) Option {
return func(c *Cow) error {
c.eyes = adjustTo2Chars(s)
return nil
}
}
// Tongue specifies tongue
// The specified string will always be adjusted to be less than or equal to two characters.
func Tongue(s string) Option {
return func(c *Cow) error {
c.tongue = adjustTo2Chars(s)
return nil
}
}
func adjustTo2Chars(s string) string {
if len(s) >= 2 {
return s[:2]
}
if len(s) == 1 {
return s + " "
}
return " "
}
func containCows(target string) (*CowFile, error) {
cowPaths, err := Cows()
if err != nil {
return nil, err
}
for _, cowPath := range cowPaths {
cowfile, ok := cowPath.Lookup(target)
if ok {
return cowfile, nil
}
}
return nil, nil
}
// NotFound is indicated not found the cowfile.
type NotFound struct {
Cowfile string
}
var _ error = (*NotFound)(nil)
func (n *NotFound) Error() string {
return fmt.Sprintf("not found %q cowfile", n.Cowfile)
}
// Type specify name of the cowfile
func Type(s string) Option {
if s == "" {
s = "default"
}
return func(c *Cow) error {
cowfile, err := containCows(s)
if err != nil {
return err
}
if cowfile != nil {
c.typ = cowfile
return nil
}
return &NotFound{Cowfile: s}
}
}
// Thinking enables thinking mode
func Thinking() Option {
return func(c *Cow) error {
c.thinking = true
return nil
}
}
// Thoughts Thoughts allows you to specify
// the rune that will be drawn between
// the speech bubbles and the cow
func Thoughts(thoughts rune) Option {
return func(c *Cow) error {
c.thoughts = thoughts
return nil
}
}
// Random specifies something .cow from cows directory
func Random() Option {
pick, err := pickCow()
return func(c *Cow) error {
if err != nil {
return err
}
c.typ = pick
return nil
}
}
func pickCow() (*CowFile, error) {
cowPaths, err := Cows()
if err != nil {
return nil, err
}
cowPath := cowPaths[rand.Intn(len(cowPaths))]
n := len(cowPath.CowFiles)
cowfile := cowPath.CowFiles[rand.Intn(n)]
return &CowFile{
Name: cowfile,
BasePath: cowPath.Name,
LocationType: cowPath.LocationType,
}, nil
}
// BallonWidth specifies ballon size
func BallonWidth(size uint) Option {
return func(c *Cow) error {
c.ballonWidth = int(size)
return nil
}
}
// DisableWordWrap disables word wrap.
// Ignoring width of the ballon.
func DisableWordWrap() Option {
return func(c *Cow) error {
c.disableWordWrap = true
return nil
}
}
================================================
FILE: cow_test.go
================================================
package cowsay
import (
"errors"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func TestCow_Clone(t *testing.T) {
tests := []struct {
name string
opts []Option
from *Cow
want *Cow
}{
{
name: "without options",
opts: []Option{},
from: func() *Cow {
cow, _ := New()
return cow
}(),
want: func() *Cow {
cow, _ := New()
return cow
}(),
},
{
name: "with some options",
opts: []Option{},
from: func() *Cow {
cow, _ := New(
Type("docker"),
BallonWidth(60),
)
return cow
}(),
want: func() *Cow {
cow, _ := New(
Type("docker"),
BallonWidth(60),
)
return cow
}(),
},
{
name: "clone and some options",
opts: []Option{
Thinking(),
Thoughts('o'),
},
from: func() *Cow {
cow, _ := New(
Type("docker"),
BallonWidth(60),
)
return cow
}(),
want: func() *Cow {
cow, _ := New(
Type("docker"),
BallonWidth(60),
Thinking(),
Thoughts('o'),
)
return cow
}(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.want.Clone(tt.opts...)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(tt.want, got,
cmp.AllowUnexported(Cow{}),
cmpopts.IgnoreFields(Cow{}, "buf")); diff != "" {
t.Errorf("(-want, +got)\n%s", diff)
}
})
}
t.Run("random", func(t *testing.T) {
cow, _ := New(
Type(""),
Thinking(),
Thoughts('o'),
Eyes("xx"),
Tongue("u"),
Random(),
)
cloned, _ := cow.Clone()
if diff := cmp.Diff(cow, cloned,
cmp.AllowUnexported(Cow{}),
cmpopts.IgnoreFields(Cow{}, "buf")); diff != "" {
t.Errorf("(-want, +got)\n%s", diff)
}
})
t.Run("error", func(t *testing.T) {
cow, err := New()
if err != nil {
t.Fatal(err)
}
wantErr := errors.New("error")
_, err = cow.Clone(func(*Cow) error {
return wantErr
})
if wantErr != err {
t.Fatalf("want %v, but got %v", wantErr, err)
}
})
}
func Test_adjustTo2Chars(t *testing.T) {
tests := []struct {
name string
s string
want string
}{
{
name: "empty",
s: "",
want: " ",
},
{
name: "1 character",
s: "1",
want: "1 ",
},
{
name: "2 characters",
s: "12",
want: "12",
},
{
name: "3 characters",
s: "123",
want: "12",
},
}
for _, tt := range tests {
t.Run(tt.s, func(t *testing.T) {
if got := adjustTo2Chars(tt.s); got != tt.want {
t.Errorf("adjustTo2Chars() = %v, want %v", got, tt.want)
}
})
}
}
func TestNotFound_Error(t *testing.T) {
file := "test"
n := &NotFound{
Cowfile: file,
}
want := fmt.Sprintf("not found %q cowfile", file)
if want != n.Error() {
t.Fatalf("want %q but got %q", want, n.Error())
}
}
================================================
FILE: cows/beavis.zen.cow
================================================
##
## Beavis, with Zen philosophy removed.
##
$the_cow = <<EOC;
$thoughts __------~~-,
$thoughts ,' ,
/ \\
/ :
| '
| |
| |
| _-- |
_| =-. .-. ||
o|/o/ _. |
/ ~ \\ |
(____\@) ___~ |
|_===~~~.` |
_______.--~ |
\\________ |
\\ |
__/-___-- -__
/ _ \\
EOC
================================================
FILE: cows/bong.cow
================================================
##
## A cow with a bong, from lars@csua.berkeley.edu
##
$the_cow = <<EOC;
$thoughts
$thoughts
^__^
_______/($eyes)
/\\/( /(__)
| W----|| |~|
|| || |~| ~~
|~| ~
|_| o
|#|/
_+#+_
EOC
================================================
FILE: cows/bud-frogs.cow
================================================
##
## The Budweiser frogs
##
$the_cow = <<EOC;
$thoughts
$thoughts
oO)-. .-(Oo
/__ _\\ /_ __\\
\\ \\( | ()~() | )/ /
\\__|\\ | (-___-) | /|__/
' '--' ==`-'== '--' '
EOC
================================================
FILE: cows/bunny.cow
================================================
##
## A cute little wabbit
##
$the_cow = <<EOC;
$thoughts
$thoughts \\
\\ /\\
( )
.( o ).
EOC
================================================
FILE: cows/cheese.cow
================================================
##
## The cheese from milk & cheese
##
$the_cow = <<EOC;
$thoughts
$thoughts
_____ _________
/ \\_/ |
| ||
| ||
| ###\\ /### | |
| 0 \\/ 0 | |
/| | |
/ | < |\\ \\
| /| | | |
| | \\_______/ | | |
| | | / /
/|| /|||
----------------|
| | | |
*** ***
/___\\ /___\\
EOC
================================================
FILE: cows/cower.cow
================================================
##
## A cowering cow
##
$the_cow = <<EOC;
$thoughts
$thoughts
,__, | |
(oo)\\| |___
(__)\\| | )\\_
| |_w | \\
| | || *
Cower....
EOC
================================================
FILE: cows/daemon.cow
================================================
##
## 4.4 >> 5.4
##
$the_cow = <<EOC;
$thoughts , ,
$thoughts /( )`
$thoughts \\ \\___ / |
/- _ `-/ '
(/\\/ \\ \\ /\\
/ / | ` \\
O O ) / |
`-^--'`< '
(_.) _ ) /
`.___/` /
`-----' /
<----. __ / __ \\
<----|====O)))==) \\) /====
<----' `--' `.__,' \\
| |
\\ /
______( (_ / \\______
,' ,-----' | \\
`--{__________) \\/
EOC
================================================
FILE: cows/default.cow
================================================
$the_cow = <<EOC;
$thoughts ^__^
$thoughts ($eyes)\\_______
(__)\\ )\\/\\
$tongue ||----w |
|| ||
EOC
================================================
FILE: cows/deno.cow
================================================
## https://github.com/kawarimidoll/deno-cli-tools/blob/30107adad1dd7d00da8be08c76f843bbfd88b455/denosay/mod.ts#L64-L73
$the_cow = <<EOC;
$thoughts
$thoughts _
( ・ヽ
\ \
⎞ \
| `ヽ
⎩ ト、
u¯u︶u
EOC
================================================
FILE: cows/docker.cow
================================================
##
## Docker Cow
##
$the_cow = <<EOC;
$thoughts
$thoughts
$thoughts
## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
{ / ===-
\______ O __/
\ \ __/
\____\_______/
EOC
================================================
FILE: cows/dragon-and-cow.cow
================================================
##
## A dragon smiting a cow, possible credit to kube@csua.berkeley.edu
##
$the_cow = <<EOC;
$thoughts ^ /^
$thoughts / \\ // \\
$thoughts |\\___/| / \\// .\\
$thoughts /O O \\__ / // | \\ \\ *----*
/ / \\/_/ // | \\ \\ \\ |
\@___\@` \\/_ // | \\ \\ \\/\\ \\
0/0/| \\/_ // | \\ \\ \\ \\
0/0/0/0/| \\/// | \\ \\ | |
0/0/0/0/0/_|_ / ( // | \\ _\\ | /
0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\\.-~ / /
,-} _ *-.|.-~-. .~ ~
\\ \\__/ `/\\ / ~-. _ .-~ /
\\____($eyes) *. } { /
( (--) .----~-.\\ \\-` .~
//__\\\\ \\__ Ack! ///.----..< \\ _ -~
// \\\\ ///-._ _ _ _ _ _ _{^ - - - - ~
EOC
================================================
FILE: cows/dragon.cow
================================================
##
## The Whitespace Dragon
##
$the_cow = <<EOC;
$thoughts / \\ //\\
$thoughts |\\___/| / \\// \\\\
/0 0 \\__ / // | \\ \\
/ / \\/_/ // | \\ \\
\@_^_\@'/ \\/_ // | \\ \\
//_^_/ \\/_ // | \\ \\
( //) | \\/// | \\ \\
( / /) _|_ / ) // | \\ _\\
( // /) '/,_ _ _/ ( ; -. | _ _\\.-~ .-~~~^-.
(( / / )) ,-{ _ `-.|.-~-. .~ `.
(( // / )) '/\\ / ~-. _ .-~ .-~^-. \\
(( /// )) `. { } / \\ \\
(( / )) .----~-.\\ \\-' .~ \\ `. \\^-.
///.----..> \\ _ -~ `. ^-` ^-_
///-._ _ _ _ _ _ _}^ - - - - ~ ~-- ,.-~
/.-~
EOC
================================================
FILE: cows/elephant-in-snake.cow
================================================
##
## Do we need to explain this?
##
$the_cow = <<EOC;
$thoughts
$thoughts ....
........ .
. .
. .
......... .......
..............................
Elephant inside ASCII snake
EOC
================================================
FILE: cows/elephant.cow
================================================
##
## An elephant out and about
##
$the_cow = <<EOC;
$thoughts /\\ ___ /\\
$thoughts // \\/ \\/ \\\\
(( O O ))
\\\\ / \\ //
\\/ | | \\/
| | | |
| | | |
| o |
| | | |
|m| |m|
EOC
================================================
FILE: cows/eyes.cow
================================================
##
## Evil-looking eyes
##
$the_cow = <<EOC;
$thoughts
$thoughts
.::!!!!!!!:.
.!!!!!:. .:!!!!!!!!!!!!
~~~~!!!!!!. .:!!!!!!!!!UWWW\$\$\$
:\$\$NWX!!: .:!!!!!!XUWW\$\$\$\$\$\$\$\$\$P
\$\$\$\$\$##WX!: .<!!!!UW\$\$\$\$" \$\$\$\$\$\$\$\$#
\$\$\$\$\$ \$\$\$UX :!!UW\$\$\$\$\$\$\$\$\$ 4\$\$\$\$\$*
^\$\$\$B \$\$\$\$\\ \$\$\$\$\$\$\$\$\$\$\$\$ d\$\$R"
"*\$bd\$\$\$\$ '*\$\$\$\$\$\$\$\$\$\$\$o+#"
"""" """""""
EOC
================================================
FILE: cows/flaming-sheep.cow
================================================
##
## The flaming sheep, contributed by Geordan Rosario (geordan@csua.berkeley.edu)
##
$the_cow = <<EOC;
$thoughts . . .
$thoughts . . . ` ,
$thoughts .; . : .' : : : .
$thoughts i..`: i` i.i.,i i .
$thoughts `,--.|i |i|ii|ii|i:
U${eyes}U\\.'\@\@\@\@\@\@`.||'
\\__/(\@\@\@\@\@\@\@\@\@\@)'
(\@\@\@\@\@\@\@\@)
`YY~~~~YY'
|| ||
EOC
================================================
FILE: cows/ghostbusters.cow
================================================
##
## Ghostbusters!
##
$the_cow = <<EOC;
$thoughts
$thoughts
$thoughts __---__
_- /--______
__--( / \\ )XXXXXXXXXXX\\v.
.-XXX( O O )XXXXXXXXXXXXXXX-
/XXX( U ) XXXXXXX\\
/XXXXX( )--_ XXXXXXXXXXX\\
/XXXXX/ ( O ) XXXXXX \\XXXXX\\
XXXXX/ / XXXXXX \\__ \\XXXXX
XXXXXX__/ XXXXXX \\__---->
---___ XXX__/ XXXXXX \\__ /
\\- --__/ ___/\\ XXXXXX / ___--/=
\\-\\ ___/ XXXXXX '--- XXXXXX
\\-\\/XXX\\ XXXXXX /XXXXX
\\XXXXXXXXX \\ /XXXXX/
\\XXXXXX > _/XXXXX/
\\XXXXX--__/ __-- XXXX/
-XXXXXXXX--------------- XXXXXX-
\\XXXXXXXXXXXXXXXXXXXXXXXXXX/
""VXXXXXXXXXXXXXXXXXXV""
EOC
================================================
FILE: cows/gopher.cow
================================================
$the_cow = <<EOC;
$thoughts
$thoughts ,_---~~~~~----._
_,,_,*^____ _____``*g*\\"*,
/ __/ /' ^. / \\ ^@q f
[ @f | @)) | | @)) l 0 _/
\`/ \~____ / __ \_____/ \\
| _l__l_ I
| [______] I
| | | | |
| ~ ~ |
| |
| |
EOC
================================================
FILE: cows/head-in.cow
================================================
##
## Go stick yer head in a cow.
##
$the_cow = <<EOC;
$thoughts
$thoughts
^__^ /
($eyes)\\_______/ _________
(__)\\ )=( ____|_ \\_____
$tongue ||----w | \\ \\ \\_____ |
|| || || ||
EOC
================================================
FILE: cows/hellokitty.cow
================================================
##
## Hello Kitty
##
$the_cow = <<EOC;
$thoughts
$thoughts
/\\_)o<
| \\
| O . O|
\\_____/
EOC
================================================
FILE: cows/kiss.cow
================================================
##
## A lovers' empbrace
##
$the_cow = <<EOC;
$thoughts
$thoughts
,;;;;;;;,
;;;;;;;;;;;,
;;;;;'_____;'
;;;(/))))|((\\
_;;((((((|))))
/ |_\\\\\\\\\\\\\\\\\\\\\\\\
.--~( \\ ~))))))))))))
/ \\ `\\-(((((((((((\\\\
| | `\\ ) |\\ /|)
| | `. _/ \\_____/ |
| , `\\~ /
| \\ \\ /
| `. `\\| /
| ~- `\\ /
\\____~._/~ -_, (\\
|-----|\\ \\ ';;
| | :;;;' \\
| / | |
| | |
EOC
================================================
FILE: cows/kitty.cow
================================================
##
## A kitten of sorts, I think
##
$the_cow = <<EOC;
$thoughts
$thoughts
("`-' '-/") .___..--' ' "`-._
` *_ * ) `-. ( ) .`-.__. `)
(_Y_.) ' ._ ) `._` ; `` -. .-'
_.. `--'_..-_/ /--' _ .' ,4
( i l ),-'' ( l i),' ( ( ! .-'
EOC
================================================
FILE: cows/koala.cow
================================================
##
## From the canonical koala collection
##
$the_cow = <<EOC;
$thoughts
$thoughts
___
{~._.~}
( Y )
()~*~()
(_)-(_)
EOC
================================================
FILE: cows/kosh.cow
================================================
##
## It's a Kosh Cow!
##
$the_cow = <<EOC;
$thoughts
$thoughts
$thoughts
___ _____ ___
/ \\ / /| / \\
| | / / | | |
| | /____/ | | |
| | | | | | |
| | | {} | / | |
| | |____|/ | |
| | |==| | |
| \\___________/ |
| |
| |
EOC
================================================
FILE: cows/luke-koala.cow
================================================
##
## From the canonical koala collection
##
$the_cow = <<EOC;
$thoughts
$thoughts .
___ //
{~._.~}//
( Y )K/
()~*~()
(_)-(_)
Luke
Sywalker
koala
EOC
================================================
FILE: cows/meow.cow
================================================
##
## A meowing tiger?
##
$the_cow = <<EOC;
$thoughts
$thoughts , _ ___.--'''`--''//-,-_--_.
\\`"' ` || \\\\ \\ \\\\/ / // / ,-\\\\`,_
/'` \\ \\ || Y | \\|/ / // / - |__ `-,
/\@"\\ ` \\ `\\ | | ||/ // | \\/ \\ `-._`-,_.,
/ _.-. `.-\\,___/\\ _/|_/_\\_\\/|_/ | `-._._)
`-'``/ / | // \\__/\\__ / \\__/ \\
`-' /-\\/ | -| \\__ \\ |-' |
__/\\ / _/ \\/ __,-' ) ,' _|'
(((__/(((_.' ((___..-'((__,'
EOC
================================================
FILE: cows/milk.cow
================================================
##
## Milk from Milk and Cheese
##
$the_cow = <<EOC;
$thoughts ____________
$thoughts |__________|
/ /\\
/ / \\
/___________/___/|
| | |
| ==\\ /== | |
| O O | \\ \\ |
| < | \\ \\|
/| | \\ \\
/ | \\_____/ | / /
/ /| | / /|
/||\\| | /||\\/
-------------|
| | | |
<__/ \\__>
EOC
================================================
FILE: cows/moofasa.cow
================================================
##
## MOOfasa.
##
$the_cow = <<EOC;
$thoughts ____
$thoughts / \\
| ^__^ |
| ($eyes) |______
| (__) | )\\/\\
\\____/|----w |
|| ||
Moofasa
EOC
================================================
FILE: cows/moose.cow
================================================
$the_cow = <<EOC;
$thoughts
$thoughts \\_\\_ _/_/
$thoughts \\__/
($eyes)\\_______
(__)\\ )\\/\\
$tongue ||----w |
|| ||
EOC
================================================
FILE: cows/mutilated.cow
================================================
##
## A mutilated cow, from aspolito@csua.berkeley.edu
##
$the_cow = <<EOC;
$thoughts \\_______
v__v $thoughts \\ O )
($eyes) ||----w |
(__) || || \\/\\
$tongue
EOC
================================================
FILE: cows/ren.cow
================================================
##
## Ren
##
$the_cow = <<EOC;
$thoughts
$thoughts
____
/# /_\\_
| |/o\\o\\
| \\\\_/_/
/ |_ |
| ||\\_ ~|
| ||| \\/
| |||_
\\// |
|| |
||_ \\
\\_| o|
/\\___/
/ ||||__
(___)_)
EOC
================================================
FILE: cows/sage.cow
================================================
## http://www.chris.com/ascii/joan/www.geocities.com/SoHo/7373/men.html#confucious
$the_cow = <<EOC;
$thoughts
$thoughts
.
.:::.
.:::::::.
V^V^V^V^V^V
(| ^ ^ |)
| (_) |
`//=\\'
(((()))
)))((
(())))
))((
(()
))
(
EOC
================================================
FILE: cows/satanic.cow
================================================
##
## Satanic cow, source unknown.
##
$the_cow = <<EOC;
$thoughts
$thoughts (__)
(\\/)
/-------\\/
/ | 666 ||
* ||----||
~~ ~~
EOC
================================================
FILE: cows/sheep.cow
================================================
##
## The non-flaming sheep.
##
$the_cow = <<EOC
$thoughts
$thoughts
__
U${eyes}U\\.'\@\@\@\@\@\@`.
\\__/(\@\@\@\@\@\@\@\@\@\@)
(\@\@\@\@\@\@\@\@)
`YY~~~~YY'
|| ||
EOC
================================================
FILE: cows/skeleton.cow
================================================
##
## This 'Scowleton' brought to you by one of
## {appel,kube,rowe}@csua.berkeley.edu
##
$the_cow = <<EOC;
$thoughts (__)
$thoughts /$eyes|
$thoughts (_"_)*+++++++++*
//I#\\\\\\\\\\\\\\\\I\\
I[I|I|||||I I `
I`I'///'' I I
I I I I
~ ~ ~ ~
Scowleton
EOC
================================================
FILE: cows/small.cow
================================================
##
## A small cow, artist unknown
##
$the_cow = <<EOC;
$thoughts ,__,
$thoughts ($eyes)____
(__) )\\
$tongue||--|| *
EOC
================================================
FILE: cows/sodomized.cow
================================================
##
## A cow performing an unnatural act, artist unknown.
##
$the_cow = <<EOC;
$thoughts _
$thoughts (_)
$thoughts ^__^ / \\
$thoughts ($eyes)\\_____/_\\ \\
(__)\\ ) /
$tongue ||----w ((
|| ||>>
EOC
================================================
FILE: cows/squirrel.cow
================================================
$the_cow = <<EOC;
$thoughts
$thoughts
_ _
| \__/| .~ ~.
/$eyes `./ .'
{o__, \ {
/ . . ) \
`-` '-' \ }
.( _( )_.'
'---.~_ _ _|
EOC
================================================
FILE: cows/stegosaurus.cow
================================================
##
## A stegosaur with a top hat?
##
$the_cow = <<EOC;
$thoughts . .
$thoughts / `. .' "
$thoughts .---. < > < > .---.
$thoughts | \\ \\ - ~ ~ - / / |
_____ ..-~ ~-..-~
| | \\~~~\\.' `./~~~/
--------- \\__/ \\__/
.' O \\ / / \\ "
(_____, `._.' | } \\/~~~/
`----. / } | / \\__/
`-. | / | / `. ,~~|
~-.__| /_ - ~ ^| /- _ `..-'
| / | / ~-. `-. _ _ _
|_____| |_____| ~ - . _ _ _ _ _>
EOC
================================================
FILE: cows/stimpy.cow
================================================
##
## Stimpy!
##
$the_cow = <<EOC;
$thoughts . _ .
$thoughts |\\_|/__/|
/ / \\/ \\ \\
/__|O||O|__ \\
|/_ \\_/\\_/ _\\ |
| | (____) | ||
\\/\\___/\\__/ //
(_/ ||
| ||
| ||\\
\\ //_/
\\______//
__ || __||
(____(____)
EOC
================================================
FILE: cows/supermilker.cow
================================================
##
## A cow being milked, probably from Lars Smith (lars@csua.berkeley.edu)
##
$the_cow = <<EOC;
$thoughts ^__^
$thoughts ($eyes)\\_______ ________
(__)\\ )\\/\\ |Super |
$tongue ||----W | |Milker|
|| UDDDDDDDDD|______|
EOC
================================================
FILE: cows/surgery.cow
================================================
##
## A cow operation, artist unknown
##
$the_cow = <<EOC;
$thoughts \\ /
$thoughts \\/
(__) /\\
($eyes) O O
_\\/_ //
* ( ) //
\\ (\\\\ //
\\( \\\\ )
( \\\\ ) /\\
___[\\______/^^^^^^^\\__/) o-)__
|\\__[=======______//________)__\\
\\|_______________//____________|
||| || //|| |||
||| || @.|| |||
|| \\/ .\\/ ||
. .
'.'.`
COW-OPERATION
EOC
================================================
FILE: cows/telebears.cow
================================================
##
## A cow performing an unnatural act, artist unknown.
##
$the_cow = <<EOC;
$thoughts _
$thoughts (_) <-- TeleBEARS
$thoughts ^__^ / \\
$thoughts ($eyes)\\_____/_\\ \\
(__)\\ you ) /
$tongue ||----w ((
|| ||>>
EOC
================================================
FILE: cows/turkey.cow
================================================
##
## Turkey!
##
$the_cow = <<EOC;
$thoughts ,+*^^*+___+++_
$thoughts ,*^^^^ )
$thoughts _+* ^**+_
$thoughts +^ _ _++*+_+++_, )
_+^^*+_ ( ,+*^ ^ \\+_ )
{ ) ( ,( ,_+--+--, ^) ^\\
{ (\@) } f ,( ,+-^ __*_*_ ^^\\_ ^\\ )
{:;-/ (_+*-+^^^^^+*+*<_ _++_)_ ) ) /
( / ( ( ,___ ^*+_+* ) < < \\
U _/ ) *--< ) ^\\-----++__) ) ) )
( ) _(^)^^)) ) )\\^^^^^))^*+/ / /
( / (_))_^)) ) ) ))^^^^^))^^^)__/ +^^
( ,/ (^))^)) ) ) ))^^^^^^^))^^) _)
*+__+* (_))^) ) ) ))^^^^^^))^^^^^)____*^
\\ \\_)^)_)) ))^^^^^^^^^^))^^^^)
(_ ^\\__^^^^^^^^^^^^))^^^^^^^)
^\\___ ^\\__^^^^^^))^^^^^^^^)\\\\
^^^^^\\uuu/^^\\uuu/^^^^\\^\\^\\^\\^\\^\\^\\^\\
___) >____) >___ ^\\_\\_\\_\\_\\_\\_\\)
^^^//\\\\_^^//\\\\_^ ^(\\_\\_\\_\\)
^^^ ^^ ^^^ ^
EOC
================================================
FILE: cows/turtle.cow
================================================
##
## A mysterious turtle...
##
$the_cow = <<EOC;
$thoughts ___-------___
$thoughts _-~~ ~~-_
$thoughts _-~ /~-_
/^\\__/^\\ /~ \\ / \\
/| O|| O| / \\_______________/ \\
| |___||__| / / \\ \\
| \\ / / \\ \\
| (_______) /______/ \\_________ \\
| / / \\ / \\
\\ \\^\\\\ \\ / \\ /
\\ || \\______________/ _-_ //\\__//
\\ ||------_-~~-_ ------------- \\ --/~ ~\\ || __/
~-----||====/~ |==================| |/~~~~~
(_(__/ ./ / \\_\\ \\.
(_(___/ \\_____)_)
EOC
================================================
FILE: cows/tux.cow
================================================
##
## TuX
## (c) pborys@p-soft.silesia.linux.org.pl
##
$the_cow = <<EOC;
$thoughts
$thoughts
.--.
|o_o |
|:_/ |
// \\ \\
(| | )
/'\\_ _/`\\
\\___)=(___/
EOC
================================================
FILE: cows/vader-koala.cow
================================================
##
## Another canonical koala?
##
$the_cow = <<EOC;
$thoughts
$thoughts .
.---. //
Y|o o|Y//
/_(i=i)K/
~()~*~()~
(_)-(_)
Darth
Vader
koala
EOC
================================================
FILE: cows/vader.cow
================================================
##
## Cowth Vader, from geordan@csua.berkeley.edu
##
$the_cow = <<EOC;
$thoughts ,-^-.
$thoughts !oYo!
$thoughts /./=\\.\\______
## )\\/\\
||-----w||
|| ||
Cowth Vader
EOC
================================================
FILE: cows/www.cow
================================================
##
## A cow wadvertising the World Wide Web, from lim@csua.berkeley.edu
##
$the_cow = <<EOC;
$thoughts ^__^
$thoughts ($eyes)\\_______
(__)\\ )\\/\\
$tongue ||--WWW |
|| ||
EOC
================================================
FILE: cowsay.go
================================================
package cowsay
import (
"io/ioutil"
"math/rand"
"os"
"path"
"path/filepath"
"sort"
"strings"
"time"
)
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
// Say to return cowsay string.
func Say(phrase string, options ...Option) (string, error) {
cow, err := New(options...)
if err != nil {
return "", err
}
return cow.Say(phrase)
}
// LocationType indicates the type of COWPATH.
type LocationType int
const (
// InBinary indicates the COWPATH in binary.
InBinary LocationType = iota
// InDirectory indicates the COWPATH in your directory.
InDirectory
)
// CowPath is information of the COWPATH.
type CowPath struct {
// Name is name of the COWPATH.
// If you specified `COWPATH=/foo/bar`, Name is `/foo/bar`.
Name string
// CowFiles are name of the cowfile which are trimmed ".cow" suffix.
CowFiles []string
// LocationType is the type of COWPATH
LocationType LocationType
}
// Lookup will look for the target cowfile in the specified path.
// If it exists, it returns the cowfile information and true value.
func (c *CowPath) Lookup(target string) (*CowFile, bool) {
for _, cowfile := range c.CowFiles {
if cowfile == target {
return &CowFile{
Name: cowfile,
BasePath: c.Name,
LocationType: c.LocationType,
}, true
}
}
return nil, false
}
// CowFile is information of the cowfile.
type CowFile struct {
// Name is name of the cowfile.
Name string
// BasePath is the path which the cowpath is in.
BasePath string
// LocationType is the type of COWPATH
LocationType LocationType
}
// ReadAll reads the cowfile content.
// If LocationType is InBinary, the file read from binary.
// otherwise reads from file system.
func (c *CowFile) ReadAll() ([]byte, error) {
if c.LocationType == InBinary {
// go embed is used "/" separator
joinedPath := path.Join(c.BasePath, c.Name+".cow")
return Asset(joinedPath)
}
joinedPath := filepath.Join(c.BasePath, c.Name+".cow")
return ioutil.ReadFile(joinedPath)
}
// Cows to get list of cows
func Cows() ([]*CowPath, error) {
cowPaths, err := cowsFromCowPath()
if err != nil {
return nil, err
}
cowPaths = append(cowPaths, &CowPath{
Name: "cows",
CowFiles: CowsInBinary(),
LocationType: InBinary,
})
return cowPaths, nil
}
func cowsFromCowPath() ([]*CowPath, error) {
cowPaths := make([]*CowPath, 0)
cowPath := os.Getenv("COWPATH")
if cowPath == "" {
return cowPaths, nil
}
paths := splitPath(cowPath)
for _, path := range paths {
dirEntries, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
path := &CowPath{
Name: path,
CowFiles: []string{},
LocationType: InDirectory,
}
for _, entry := range dirEntries {
name := entry.Name()
if strings.HasSuffix(name, ".cow") {
name = strings.TrimSuffix(name, ".cow")
path.CowFiles = append(path.CowFiles, name)
}
}
sort.Strings(path.CowFiles)
cowPaths = append(cowPaths, path)
}
return cowPaths, nil
}
// GetCow to get cow's ascii art
func (cow *Cow) GetCow() (string, error) {
src, err := cow.typ.ReadAll()
if err != nil {
return "", err
}
r := strings.NewReplacer(
"\\\\", "\\",
"\\@", "@",
"\\$", "$",
"$eyes", cow.eyes,
"${eyes}", cow.eyes,
"$tongue", cow.tongue,
"${tongue}", cow.tongue,
"$thoughts", string(cow.thoughts),
"${thoughts}", string(cow.thoughts),
)
newsrc := r.Replace(string(src))
separate := strings.Split(newsrc, "\n")
mow := make([]string, 0, len(separate))
for _, line := range separate {
if strings.Contains(line, "$the_cow = <<EOC") || strings.HasPrefix(line, "##") {
continue
}
if strings.HasPrefix(line, "EOC") {
break
}
mow = append(mow, line)
}
return strings.Join(mow, "\n"), nil
}
================================================
FILE: cowsay_test.go
================================================
package cowsay
import (
"bytes"
"errors"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func TestCows(t *testing.T) {
t.Run("no set COWPATH env", func(t *testing.T) {
cowPaths, err := Cows()
if err != nil {
t.Fatal(err)
}
if len(cowPaths) != 1 {
t.Fatalf("want 1, but got %d", len(cowPaths))
}
cowPath := cowPaths[0]
if len(cowPath.CowFiles) == 0 {
t.Fatalf("no cowfiles")
}
wantCowPath := &CowPath{
Name: "cows",
LocationType: InBinary,
}
if diff := cmp.Diff(wantCowPath, cowPath,
cmpopts.IgnoreFields(CowPath{}, "CowFiles"),
); diff != "" {
t.Errorf("(-want, +got)\n%s", diff)
}
})
t.Run("set COWPATH env", func(t *testing.T) {
cowpath := filepath.Join("testdata", "testdir")
os.Setenv("COWPATH", cowpath)
defer os.Unsetenv("COWPATH")
cowPaths, err := Cows()
if err != nil {
t.Fatal(err)
}
if len(cowPaths) != 2 {
t.Fatalf("want 2, but got %d", len(cowPaths))
}
wants := []*CowPath{
{
Name: filepath.Join("testdata", "testdir"),
LocationType: InDirectory,
},
{
Name: "cows",
LocationType: InBinary,
},
}
if diff := cmp.Diff(wants, cowPaths,
cmpopts.IgnoreFields(CowPath{}, "CowFiles"),
); diff != "" {
t.Errorf("(-want, +got)\n%s", diff)
}
if len(cowPaths[0].CowFiles) != 1 {
t.Fatalf("unexpected cowfiles len = %d, %+v",
len(cowPaths[0].CowFiles), cowPaths[0].CowFiles,
)
}
if cowPaths[0].CowFiles[0] != "test" {
t.Fatalf("want %q but got %q", "test", cowPaths[0].CowFiles[0])
}
})
t.Run("set COWPATH env", func(t *testing.T) {
os.Setenv("COWPATH", "notfound")
defer os.Unsetenv("COWPATH")
_, err := Cows()
if err == nil {
t.Fatal("want error")
}
})
}
func TestCowPath_Lookup(t *testing.T) {
t.Run("looked for cowfile", func(t *testing.T) {
c := &CowPath{
Name: "basepath",
CowFiles: []string{"test"},
LocationType: InBinary,
}
got, ok := c.Lookup("test")
if !ok {
t.Errorf("want %v", ok)
}
want := &CowFile{
Name: "test",
BasePath: "basepath",
LocationType: InBinary,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("(-want, +got)\n%s", diff)
}
})
t.Run("no cowfile", func(t *testing.T) {
c := &CowPath{
Name: "basepath",
CowFiles: []string{"test"},
LocationType: InBinary,
}
got, ok := c.Lookup("no cowfile")
if ok {
t.Errorf("want %v", !ok)
}
if got != nil {
t.Error("want nil")
}
})
}
func TestCowFile_ReadAll(t *testing.T) {
fromTestData := &CowFile{
Name: "test",
BasePath: filepath.Join("testdata", "testdir"),
LocationType: InDirectory,
}
fromTestdataContent, err := fromTestData.ReadAll()
if err != nil {
t.Fatal(err)
}
fromBinary := &CowFile{
Name: "default",
BasePath: "cows",
LocationType: InBinary,
}
fromBinaryContent, err := fromBinary.ReadAll()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(fromTestdataContent, fromBinaryContent) {
t.Fatalf("testdata\n%s\n\nbinary%s\n", string(fromTestdataContent), string(fromBinaryContent))
}
}
const defaultSay = ` ________
< cowsay >
--------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||`
func TestSay(t *testing.T) {
type args struct {
phrase string
options []Option
}
tests := []struct {
name string
args args
wantFile string
wantErr bool
}{
{
name: "default",
args: args{
phrase: "hello!",
},
wantFile: "default.cow",
wantErr: false,
},
{
name: "nest",
args: args{
phrase: defaultSay,
options: []Option{
DisableWordWrap(),
},
},
wantFile: "nest.cow",
wantErr: false,
},
{
name: "error",
args: args{
phrase: "error",
options: []Option{
func(*Cow) error {
return errors.New("error")
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Say(tt.args.phrase, tt.args.options...)
if (err != nil) != tt.wantErr {
t.Errorf("Say() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr {
return
}
filename := filepath.Join("testdata", tt.wantFile)
content, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatal(err)
}
got = strings.Replace(got, "\r", "", -1) // for windows
want := strings.Replace(string(content), "\r", "", -1) // for windows
if want != got {
t.Log(cmp.Diff([]byte(want), []byte(got)))
t.Fatalf("want\n%s\n\ngot\n%s", want, got)
}
})
}
}
================================================
FILE: decoration/aurora.go
================================================
package decoration
import (
"fmt"
"io"
"math"
"unicode"
"unicode/utf8"
)
func (w *Writer) writeAsAurora(b []byte) (nn int, err error) {
defer w.buf.Reset()
for len(b) > 0 {
char, size := utf8.DecodeRune(b)
if char == '\n' {
w.buf.WriteRune(char)
b = b[size:]
continue
}
if unicode.IsSpace(char) {
w.buf.WriteRune(char)
} else {
fmt.Fprintf(&w.buf, "\033[38;5;%d%sm%c\033[0m",
rgb(float64(w.options.colorSeq)),
w.options.maybeBold(),
char,
)
}
w.options.colorSeq++
b = b[size:]
}
return w.writer.Write(w.buf.Bytes())
}
func (w *Writer) writeStringAsAurora(s string) (nn int, err error) {
defer w.buf.Reset()
for _, char := range s {
if char == '\n' {
w.buf.WriteRune(char)
continue
}
if unicode.IsSpace(char) {
w.buf.WriteRune(char)
} else {
fmt.Fprintf(&w.buf, "\033[38;5;%d%sm%c\033[0m",
rgb(float64(w.options.colorSeq)),
w.options.maybeBold(),
char,
)
}
w.options.colorSeq++
}
if sw, ok := w.writer.(io.StringWriter); ok {
return sw.WriteString(w.buf.String())
}
return w.writer.Write(w.buf.Bytes())
}
// https://sking7.github.io/articles/139888127.html#:~:text=value%20of%20frequency.-,Using,-out-of-phase
const (
freq = 0.01
m = math.Pi / 3
redPhase = 0
greenPhase = 2 * m
bluePhase = 4 * m
)
var rgbMemo = map[float64]int64{}
func rgb(i float64) int64 {
if v, ok := rgbMemo[i]; ok {
return v
}
red := int64(6*(math.Sin(freq*i+redPhase)*127+128)/256) * 36
green := int64(6*(math.Sin(freq*i+greenPhase)*127+128)/256) * 6
blue := int64(6*(math.Sin(freq*i+bluePhase)*127+128)/256) * 1
rgbMemo[i] = 16 + red + green + blue
return rgbMemo[i]
}
================================================
FILE: decoration/bold.go
================================================
package decoration
import (
"fmt"
"io"
"unicode"
"unicode/utf8"
)
func (w *Writer) writeAsDefaultBold(b []byte) (nn int, err error) {
defer w.buf.Reset()
for len(b) > 0 {
char, size := utf8.DecodeRune(b)
if char == '\n' {
w.buf.WriteRune(char)
b = b[size:]
continue
}
if unicode.IsSpace(char) {
w.buf.WriteRune(char)
} else {
fmt.Fprintf(&w.buf, "\x1b[1m%c\x1b[0m", char)
}
b = b[size:]
}
return w.writer.Write(w.buf.Bytes())
}
func (w *Writer) writeStringAsDefaultBold(s string) (nn int, err error) {
defer w.buf.Reset()
for _, char := range s {
if char == '\n' {
w.buf.WriteRune(char)
continue
}
if unicode.IsSpace(char) {
w.buf.WriteRune(char)
} else {
fmt.Fprintf(&w.buf, "\x1b[1m%c\x1b[0m", char)
}
}
if sw, ok := w.writer.(io.StringWriter); ok {
return sw.WriteString(w.buf.String())
}
return w.writer.Write(w.buf.Bytes())
}
================================================
FILE: decoration/decoration.go
================================================
package decoration
import (
"bytes"
"io"
)
type options struct {
withBold bool
withRainbow bool
withAurora bool
colorSeq int
}
func (o *options) maybeBold() string {
if o.withBold {
return ";1"
}
return ""
}
// Option for any writer in this package.
type Option func(o *options)
// WithBold writes with bold.
func WithBold() Option {
return func(o *options) {
o.withBold = true
}
}
// WithRainbow writes with rainbow.
func WithRainbow() Option {
return func(o *options) {
o.withRainbow = true
}
}
// WithAurora writes with aurora.
func WithAurora(initialSeq int) Option {
return func(o *options) {
o.withAurora = true
o.colorSeq = initialSeq
}
}
// Writer is a writer to decorates.
type Writer struct {
writer io.Writer
buf bytes.Buffer
options *options
}
var _ interface {
io.Writer
io.StringWriter
} = (*Writer)(nil)
// NewWriter creates a new writer.
func NewWriter(w io.Writer, opts ...Option) *Writer {
options := new(options)
for _, optFunc := range opts {
optFunc(options)
}
return &Writer{
writer: w,
options: options,
}
}
// SetColorSeq sets current color sequence.
func (w *Writer) SetColorSeq(colorSeq int) {
w.options.colorSeq = colorSeq
}
// Write writes bytes. which is implemented io.Writer.
//
// If Bold is enabled in the options, the text will be written as Bold.
// If both Aurora and Rainbow are enabled in the options, Aurora will take precedence.
func (w *Writer) Write(b []byte) (nn int, err error) {
switch {
case w.options.withAurora:
return w.writeAsAurora(b)
case w.options.withRainbow:
return w.writeAsRainbow(b)
case w.options.withBold:
return w.writeAsDefaultBold(b)
default:
return w.writer.Write(b)
}
}
// WriteString writes string. which is implemented io.StringWriter.
//
// See also Write.
func (w *Writer) WriteString(s string) (n int, err error) {
switch {
case w.options.withAurora:
return w.writeStringAsAurora(s)
case w.options.withRainbow:
return w.writeStringAsRainbow(s)
case w.options.withBold:
return w.writeStringAsDefaultBold(s)
default:
if sw, ok := w.writer.(io.StringWriter); ok {
return sw.WriteString(w.buf.String())
}
return w.writer.Write([]byte(s))
}
}
================================================
FILE: decoration/rainbow.go
================================================
package decoration
import (
"fmt"
"io"
"unicode"
"unicode/utf8"
)
const (
red = iota + 31
green
yellow
blue
magenta
cyan
)
var rainbow = []int{magenta, red, yellow, green, cyan, blue}
func (w *Writer) writeAsRainbow(b []byte) (nn int, err error) {
defer w.buf.Reset()
for len(b) > 0 {
char, size := utf8.DecodeRune(b)
if char == '\n' {
w.options.colorSeq = 0
w.buf.WriteRune(char)
b = b[size:]
continue
}
if unicode.IsSpace(char) {
w.buf.WriteRune(char)
} else {
fmt.Fprintf(&w.buf, "\x1b[%d%sm%c\x1b[0m",
rainbow[w.options.colorSeq%len(rainbow)],
w.options.maybeBold(),
char,
)
}
w.options.colorSeq++
b = b[size:]
}
return w.writer.Write(w.buf.Bytes())
}
func (w *Writer) writeStringAsRainbow(s string) (nn int, err error) {
defer w.buf.Reset()
for _, char := range s {
if char == '\n' {
w.options.colorSeq = 0
w.buf.WriteRune(char)
continue
}
if unicode.IsSpace(char) {
w.buf.WriteRune(char)
} else {
fmt.Fprintf(&w.buf, "\x1b[%d%sm%c\x1b[0m",
rainbow[w.options.colorSeq%len(rainbow)],
w.options.maybeBold(),
char,
)
}
w.options.colorSeq++
}
if sw, ok := w.writer.(io.StringWriter); ok {
return sw.WriteString(w.buf.String())
}
return w.writer.Write(w.buf.Bytes())
}
================================================
FILE: doc/cowsay.1
================================================
'\" t
.\" Title: cowsay
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.16
.\" Date: 2021-11-13
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
.TH "NEO COWSAY/COWTHINK" "1" "2021-11-13" "\ \&" "\ \&"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
.nh
.ad l
.de URL
\fI\\$2\fP <\\$1>\\$3
..
.als MTO URL
.if \n[.g] \{\
. mso www.tmac
. am URL
. ad l
. .
. am MTO
. ad l
. .
. LINKSTYLE blue R < >
.\}
.SH "NAME"
Neo cowsay/cowthink \- configurable speaking/thinking cow (and a bit more)
.SH "SYNOPSIS"
.sp
cowsay [\-e \fIeye_string\fP] [\-f \fIcowfile\fP] [\-h] [\-l] [\-n] [\-T \fItongue_string\fP] [\-W \fIcolumn\fP] [\-bdgpstwy]
[\-\-random] [\-\-bold] [\-\-rainbow] [\-\-aurora] [\-\-super] [\fImessage\fP]
.SH "DESCRIPTION"
.sp
\fINeo\-cowsay\fP (cowsay) generates an ASCII picture of a cow saying something provided by the
user. If run with no arguments, it accepts standard input, word\-wraps
the message given at about 40 columns, and prints the cow saying the
given message on standard output.
.sp
To aid in the use of arbitrary messages with arbitrary whitespace,
use the \fB\-n\fP option. If it is specified, the given message will not be
word\-wrapped. This is possibly useful if you want to make the cow
think or speak in figlet(6). If \fB\-n\fP is specified, there must not be any command\-line arguments left
after all the switches have been processed.
.sp
The \fB\-W\fP specifies roughly (where the message should be wrapped. The default
is equivalent to \fB\-W 40\fP i.e. wrap words at or before the 40th column.
.sp
If any command\-line arguments are left over after all switches have
been processed, they become the cow\(cqs message. The program will not
accept standard input for a message in this case.
.sp
There are several provided modes which change the appearance of the
cow depending on its particular emotional/physical state.
.sp
The \fB\-b\fP option initiates Borg mode
.sp
\fB\-d\fP causes the cow to appear dead
.sp
\fB\-g\fP invokes greedy mode
.sp
\fB\-p\fP causes a state of paranoia to come over the cow
.sp
\fB\-s\fP makes the cow appear thoroughly stoned
.sp
\fB\-t\fP yields a tired cow
.sp
\fB\-w\fP is somewhat the opposite of \fB\-t\fP and initiates wired mode
.sp
\fB\-y\fP brings on the cow\(cqs youthful appearance.
.sp
The user may specify the \fB\-e\fP option to select the appearance of the cow\(cqs eyes, in which case
the first two characters of the argument string \fIeye_string\fP will be used. The default eyes are \fIoo\fP. The tongue is similarly
configurable through \fB\-T\fP and \fItongue_string\fP; it must be two characters and does not appear by default. However,
it does appear in the \fIdead\fP and \fIstoned\fP modes. Any configuration
done by \fB\-e\fP and \fB\-T\fP will be lost if one of the provided modes is used.
.sp
The \fB\-f\fP option specifies a particular cow picture file (\(lqcowfile\(rq) to
use. If the cowfile spec contains \fI/\fP then it will be interpreted
as a path relative to the current directory. Otherwise, cowsay
will search the path specified in the \fBCOWPATH\fP environment variable. If \fB\-f \-\fP is specified, provides
interactive Unix filter (command\-line fuzzy finder) to search the cowfile.
.sp
To list all cowfiles on the current \fBCOWPATH\fP, invoke \fBcowsay\fP with the \fB\-l\fP switch.
.sp
\fB\-\-random\fP pick randomly from available cowfiles
.sp
\fB\-\-bold\fP outputs as bold text
.sp
\fB\-\-rainbow\fP and \fB\-\-aurora\fP filters with colors an ASCII picture of a cow saying something
.sp
\fB\-\-super\fP ...enjoy!
.sp
If the program is invoked as \fBcowthink\fP then the cow will think its message instead of saying it.
.SH "COWFILE FORMAT"
.sp
A cowfile is made up of a simple block of \fBperl(1)\fP code, which assigns a picture of a cow to the variable \fB$the_cow\fP.
Should you wish to customize the eyes or the tongue of the cow,
then the variables \fB$eyes\fP and \fB$tongue\fP may be used. The trail leading up to the cow\(cqs message balloon is
composed of the character(s) in the \fB$thoughts\fP variable. Any backslashes must be reduplicated to prevent interpolation.
The name of a cowfile should end with \fB.cow ,\fP otherwise it is assumed not to be a cowfile. Also, at\-signs (\(lq@\(rq)
must be backslashed because that is what Perl 5 expects.
.SH "ENVIRONMENT"
.sp
The COWPATH environment variable, if present, will be used to search
for cowfiles. It contains a colon\-separated list of directories,
much like \fBPATH or MANPATH\fP. It should always contain the \fB/usr/local/share/cows\fP
directory, or at least a directory with a file called \fBdefault.cow\fP in it.
.SH "FILES"
.sp
\fB%PREFIX%/share/cows\fP holds a sample set of cowfiles. If your \fBCOWPATH\fP is not explicitly set, it automatically contains this directory.
.SH "BUGS"
.sp
.URL "https://github.com/Code\-Hex/Neo\-cowsay" "" ""
.sp
If there are any, please report bugs and feature requests in the issue tracker.
Please do your best to provide a reproducible test case for bugs. This should
include the \fBcowsay\fP command, the actual output and the expected output.
.SH "AUTHORS"
.sp
Neo\-cowsay author is Kei Kamikawa (\c
.MTO "x00.x7f.x86\(atgmail.com" "" ")."
.sp
The original author is Tony Monroe (\c
.MTO "tony\(atnog.net" "" "),"
with suggestions from Shannon
Appel (\c
.MTO "appel\(atCSUA.Berkeley.EDU" "" ")"
and contributions from Anthony Polito
(\c
.MTO "aspolito\(atCSUA.Berkeley.EDU" "" ")."
.SH "SEE ALSO"
.sp
perl(1), wall(1), nwrite(1), figlet(6)
================================================
FILE: doc/cowsay.1.txt.tpl
================================================
cowsay(1)
=========
Name
----
Neo cowsay/cowthink - configurable speaking/thinking cow (and a bit more)
SYNOPSIS
--------
cowsay [-e _eye_string_] [-f _cowfile_] [-h] [-l] [-n] [-T _tongue_string_] [-W _column_] [-bdgpstwy]
[--random] [--bold] [--rainbow] [--aurora] [--super] [_message_]
DESCRIPTION
-----------
_Neo-cowsay_ (cowsay) generates an ASCII picture of a cow saying something provided by the
user. If run with no arguments, it accepts standard input, word-wraps
the message given at about 40 columns, and prints the cow saying the
given message on standard output.
To aid in the use of arbitrary messages with arbitrary whitespace,
use the *-n* option. If it is specified, the given message will not be
word-wrapped. This is possibly useful if you want to make the cow
think or speak in figlet(6). If *-n* is specified, there must not be any command-line arguments left
after all the switches have been processed.
The *-W* specifies roughly (where the message should be wrapped. The default
is equivalent to *-W 40* i.e. wrap words at or before the 40th column.
If any command-line arguments are left over after all switches have
been processed, they become the cow's message. The program will not
accept standard input for a message in this case.
There are several provided modes which change the appearance of the
cow depending on its particular emotional/physical state.
The *-b* option initiates Borg mode
*-d* causes the cow to appear dead
*-g* invokes greedy mode
*-p* causes a state of paranoia to come over the cow
*-s* makes the cow appear thoroughly stoned
*-t* yields a tired cow
*-w* is somewhat the opposite of *-t* and initiates wired mode
*-y* brings on the cow's youthful appearance.
The user may specify the *-e* option to select the appearance of the cow's eyes, in which case
the first two characters of the argument string _eye_string_ will be used. The default eyes are 'oo'. The tongue is similarly
configurable through *-T* and _tongue_string_; it must be two characters and does not appear by default. However,
it does appear in the 'dead' and 'stoned' modes. Any configuration
done by *-e* and *-T* will be lost if one of the provided modes is used.
The *-f* option specifies a particular cow picture file (``cowfile'') to
use. If the cowfile spec contains '/' then it will be interpreted
as a path relative to the current directory. Otherwise, cowsay
will search the path specified in the *COWPATH* environment variable. If *-f -* is specified, provides
interactive Unix filter (command-line fuzzy finder) to search the cowfile.
To list all cowfiles on the current *COWPATH*, invoke *cowsay* with the *-l* switch.
*--random* pick randomly from available cowfiles
*--bold* outputs as bold text
*--rainbow* and *--aurora* filters with colors an ASCII picture of a cow saying something
*--super* ...enjoy!
If the program is invoked as *cowthink* then the cow will think its message instead of saying it.
COWFILE FORMAT
--------------
A cowfile is made up of a simple block of *perl(1)* code, which assigns a picture of a cow to the variable *$the_cow*.
Should you wish to customize the eyes or the tongue of the cow,
then the variables *$eyes* and *$tongue* may be used. The trail leading up to the cow's message balloon is
composed of the character(s) in the *$thoughts* variable. Any backslashes must be reduplicated to prevent interpolation.
The name of a cowfile should end with *.cow ,* otherwise it is assumed not to be a cowfile. Also, at-signs (``@'')
must be backslashed because that is what Perl 5 expects.
ENVIRONMENT
-----------
The COWPATH environment variable, if present, will be used to search
for cowfiles. It contains a colon-separated list of directories,
much like *PATH or MANPATH*. It should always contain the */usr/local/share/cows*
directory, or at least a directory with a file called *default.cow* in it.
FILES
-----
*%PREFIX%/share/cows* holds a sample set of cowfiles. If your *COWPATH* is not explicitly set, it automatically contains this directory.
BUGS
----
https://github.com/Code-Hex/Neo-cowsay
If there are any, please report bugs and feature requests in the issue tracker.
Please do your best to provide a reproducible test case for bugs. This should
include the *cowsay* command, the actual output and the expected output.
AUTHORS
-------
Neo-cowsay author is Kei Kamikawa (x00.x7f.x86@gmail.com).
The original author is Tony Monroe (tony@nog.net), with suggestions from Shannon
Appel (appel@CSUA.Berkeley.EDU) and contributions from Anthony Polito
(aspolito@CSUA.Berkeley.EDU).
SEE ALSO
--------
perl(1), wall(1), nwrite(1), figlet(6)
================================================
FILE: embed.go
================================================
package cowsay
import (
"embed"
"sort"
"strings"
)
//go:embed cows/*
var cowsDir embed.FS
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(path string) ([]byte, error) {
return cowsDir.ReadFile(path)
}
// AssetNames returns the list of filename of the assets.
func AssetNames() []string {
entries, err := cowsDir.ReadDir("cows")
if err != nil {
panic(err)
}
names := make([]string, 0, len(entries))
for _, entry := range entries {
name := strings.TrimSuffix(entry.Name(), ".cow")
names = append(names, name)
}
sort.Strings(names)
return names
}
var cowsInBinary = AssetNames()
// CowsInBinary returns the list of cowfiles which are in binary.
// the list is memoized.
func CowsInBinary() []string {
return cowsInBinary
}
================================================
FILE: examples/basic/go.mod
================================================
module github.com/Code-Hex/Neo-cowsay/v2/example
go 1.16
require github.com/Code-Hex/Neo-cowsay/v2 v2.0.1
replace github.com/Code-Hex/Neo-cowsay/v2 => ../../
================================================
FILE: examples/basic/go.sum
================================================
github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=
github.com/Code-Hex/go-wordwrap v1.0.0/go.mod h1:/SsbgkY2Q0aPQRyvXcyQwWYTQOIwSORKe6MPjRVGIWU=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
================================================
FILE: examples/basic/main.go
================================================
package main
import (
"fmt"
cowsay "github.com/Code-Hex/Neo-cowsay/v2"
)
func main() {
if false {
simple()
} else {
complex()
}
}
func simple() {
say, err := cowsay.Say(
"Hello",
cowsay.Type("default"),
cowsay.BallonWidth(40),
)
if err != nil {
panic(err)
}
fmt.Println(say)
}
func complex() {
cow, err := cowsay.New(
cowsay.BallonWidth(40),
//cowsay.Thinking(),
cowsay.Random(),
)
if err != nil {
panic(err)
}
say, err := cow.Say("Hello")
if err != nil {
panic(err)
}
fmt.Println(say)
}
================================================
FILE: examples/echo-server/README.md
================================================
## echo server
```
$ go build
$ ./echo-server
2021/11/14 16:16:27 server addr => 127.0.0.1:54456
```
## client
Use [netcat](https://en.wikipedia.org/wiki/Netcat)
Please enter `^D` (control + D) after you enter any characters.
```
$ nc 127.0.0.1 54456
hello
_______
< hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
================================================
FILE: examples/echo-server/go.mod
================================================
module github.com/Code-Hex/Neo-cowsay/v2/examples/echo-server
go 1.16
require github.com/Code-Hex/Neo-cowsay/v2 v2.0.1
replace github.com/Code-Hex/Neo-cowsay/v2 => ../../
================================================
FILE: examples/echo-server/go.sum
================================================
github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=
github.com/Code-Hex/go-wordwrap v1.0.0/go.mod h1:/SsbgkY2Q0aPQRyvXcyQwWYTQOIwSORKe6MPjRVGIWU=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
================================================
FILE: examples/echo-server/main.go
================================================
package main
import (
"context"
"io"
"log"
"net"
"os"
"os/signal"
"strings"
"time"
cowsay "github.com/Code-Hex/Neo-cowsay/v2"
)
const limit = 1000
func main() {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
log.Println("server addr =>", ln.Addr())
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
go func() {
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
return
}
conn.SetDeadline(time.Now().Add(5 * time.Second))
go func() {
defer conn.Close()
go func() {
<-ctx.Done()
// cancel
conn.SetDeadline(time.Now())
}()
var buf strings.Builder
rd := io.LimitReader(conn, limit)
if _, err := io.Copy(&buf, rd); err != nil {
log.Println("error:", err)
return
}
phrase := strings.TrimSpace(buf.String())
log.Println(phrase)
say, _ := cowsay.Say(phrase)
conn.Write([]byte(say))
}()
}
}()
<-ctx.Done()
}
================================================
FILE: go.mod
================================================
module github.com/Code-Hex/Neo-cowsay/v2
require (
github.com/Code-Hex/go-wordwrap v1.0.0
github.com/google/go-cmp v0.5.6
github.com/mattn/go-runewidth v0.0.13
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
go 1.16
================================================
FILE: go.sum
================================================
github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=
github.com/Code-Hex/go-wordwrap v1.0.0/go.mod h1:/SsbgkY2Q0aPQRyvXcyQwWYTQOIwSORKe6MPjRVGIWU=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
================================================
FILE: split.go
================================================
//go:build !windows
// +build !windows
package cowsay
import "strings"
func splitPath(s string) []string {
return strings.Split(s, ":")
}
================================================
FILE: split_windows.go
================================================
//go:build windows
// +build windows
package cowsay
import "strings"
func splitPath(s string) []string {
return strings.Split(s, ";")
}
================================================
FILE: testdata/default.cow
================================================
________
< hello! >
--------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: testdata/nest.cow
================================================
______________________________
/ ________ \
| < cowsay > |
| -------- |
| \ ^__^ |
| \ (oo)\_______ |
| (__)\ )\/\ |
| ||----w | |
\ || || /
------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
================================================
FILE: testdata/testdir/test.cow
================================================
$the_cow = <<EOC;
$thoughts ^__^
$thoughts ($eyes)\\_______
(__)\\ )\\/\\
$tongue ||----w |
|| ||
EOC
gitextract_va4xlec5/
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── dependabot.yml
│ └── workflows/
│ ├── lgtm.yml
│ ├── main.yml
│ └── release.yml
├── .gitignore
├── .goreleaser.yml
├── LICENSE
├── Makefile
├── README.md
├── balloon.go
├── bin/
│ └── .gitkeep
├── cmd/
│ ├── cmd.go
│ ├── cowsay/
│ │ └── main.go
│ ├── cowthink/
│ │ └── main.go
│ ├── go.mod
│ ├── go.sum
│ ├── internal/
│ │ ├── cli/
│ │ │ ├── cli.go
│ │ │ └── cli_test.go
│ │ ├── screen/
│ │ │ ├── buffer.go
│ │ │ └── screen.go
│ │ └── super/
│ │ └── supercow.go
│ └── testdata/
│ ├── cowsay/
│ │ ├── W_option.txt
│ │ ├── b_option.txt
│ │ ├── d_option.txt
│ │ ├── eyes_option.txt
│ │ ├── f_tux_option.txt
│ │ ├── g_option.txt
│ │ ├── n_option.txt
│ │ ├── p_option.txt
│ │ ├── s_option.txt
│ │ ├── t_option.txt
│ │ ├── tongue_option.txt
│ │ ├── wired_option.txt
│ │ └── y_option.txt
│ └── cowthink/
│ ├── W_option.txt
│ ├── b_option.txt
│ ├── d_option.txt
│ ├── eyes_option.txt
│ ├── f_tux_option.txt
│ ├── g_option.txt
│ ├── n_option.txt
│ ├── p_option.txt
│ ├── s_option.txt
│ ├── t_option.txt
│ ├── tongue_option.txt
│ ├── wired_option.txt
│ └── y_option.txt
├── cow.go
├── cow_test.go
├── cows/
│ ├── beavis.zen.cow
│ ├── bong.cow
│ ├── bud-frogs.cow
│ ├── bunny.cow
│ ├── cheese.cow
│ ├── cower.cow
│ ├── daemon.cow
│ ├── default.cow
│ ├── deno.cow
│ ├── docker.cow
│ ├── dragon-and-cow.cow
│ ├── dragon.cow
│ ├── elephant-in-snake.cow
│ ├── elephant.cow
│ ├── eyes.cow
│ ├── flaming-sheep.cow
│ ├── ghostbusters.cow
│ ├── gopher.cow
│ ├── head-in.cow
│ ├── hellokitty.cow
│ ├── kiss.cow
│ ├── kitty.cow
│ ├── koala.cow
│ ├── kosh.cow
│ ├── luke-koala.cow
│ ├── meow.cow
│ ├── milk.cow
│ ├── moofasa.cow
│ ├── moose.cow
│ ├── mutilated.cow
│ ├── ren.cow
│ ├── sage.cow
│ ├── satanic.cow
│ ├── sheep.cow
│ ├── skeleton.cow
│ ├── small.cow
│ ├── sodomized.cow
│ ├── squirrel.cow
│ ├── stegosaurus.cow
│ ├── stimpy.cow
│ ├── supermilker.cow
│ ├── surgery.cow
│ ├── telebears.cow
│ ├── turkey.cow
│ ├── turtle.cow
│ ├── tux.cow
│ ├── vader-koala.cow
│ ├── vader.cow
│ └── www.cow
├── cowsay.go
├── cowsay_test.go
├── decoration/
│ ├── aurora.go
│ ├── bold.go
│ ├── decoration.go
│ └── rainbow.go
├── doc/
│ ├── cowsay.1
│ └── cowsay.1.txt.tpl
├── embed.go
├── examples/
│ ├── basic/
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── main.go
│ └── echo-server/
│ ├── README.md
│ ├── go.mod
│ ├── go.sum
│ └── main.go
├── go.mod
├── go.sum
├── split.go
├── split_windows.go
└── testdata/
├── default.cow
├── nest.cow
└── testdir/
└── test.cow
SYMBOL INDEX (145 symbols across 21 files)
FILE: balloon.go
type border (line 11) | type border struct
method borderType (line 18) | func (cow *Cow) borderType() border {
type line (line 36) | type line struct
type lines (line 41) | type lines
method maxLineWidth (line 43) | func (cow *Cow) maxLineWidth(lines []*line) int {
method getLines (line 56) | func (cow *Cow) getLines(phrase string) []*line {
method canonicalizePhrase (line 69) | func (cow *Cow) canonicalizePhrase(phrase string) string {
method Balloon (line 81) | func (cow *Cow) Balloon(phrase string) string {
method writeBallon (line 92) | func (cow *Cow) writeBallon(lines []*line, maxWidth int) {
method flush (line 144) | func (cow *Cow) flush(text, top, bottom fmt.Stringer) string {
method padding (line 153) | func (cow *Cow) padding(line *line, maxWidth int) {
FILE: cmd/cowsay/main.go
function main (line 11) | func main() {
FILE: cmd/cowthink/main.go
function main (line 11) | func main() {
FILE: cmd/internal/cli/cli.go
function init (line 26) | func init() {
type options (line 41) | type options struct
type CLI (line 65) | type CLI struct
method program (line 73) | func (c *CLI) program() string {
method Run (line 81) | func (c *CLI) Run(argv []string) int {
method mow (line 99) | func (c *CLI) mow(argv []string) error {
method parseOptions (line 130) | func (c *CLI) parseOptions(opts *options, argv []string) ([]string, er...
method usage (line 145) | func (c *CLI) usage() []byte {
method generateOptions (line 156) | func (c *CLI) generateOptions(opts *options) []cowsay.Option {
method phrase (line 202) | func (c *CLI) phrase(opts *options, args []string) string {
method mowmow (line 214) | func (c *CLI) mowmow(opts *options, args []string) error {
function cowList (line 190) | func cowList() []string {
function selectFace (line 248) | func selectFace(opts *options, o []cowsay.Option) []cowsay.Option {
FILE: cmd/internal/cli/cli_test.go
function TestCLI_Run (line 14) | func TestCLI_Run(t *testing.T) {
FILE: cmd/internal/screen/buffer.go
function Flush (line 15) | func Flush() string {
type MoveWriter (line 21) | type MoveWriter struct
method SetPosx (line 44) | func (m *MoveWriter) SetPosx(x int) {
method Reset (line 50) | func (m *MoveWriter) Reset() {
method Write (line 56) | func (m *MoveWriter) Write(bs []byte) (nn int, _ error) {
method WriteString (line 88) | func (m *MoveWriter) WriteString(s string) (nn int, _ error) {
function NewMoveWriter (line 34) | func NewMoveWriter(w io.Writer, x, y int) *MoveWriter {
function getXY (line 120) | func getXY(x int, y int) (int, int) {
function currentHeight (line 144) | func currentHeight() int {
FILE: cmd/internal/screen/screen.go
function SaveState (line 15) | func SaveState() { Stdout.Write([]byte("\0337")) }
function RestoreState (line 18) | func RestoreState() { Stdout.Write([]byte("\0338")) }
function Clear (line 21) | func Clear() { Stdout.Write([]byte("\033[2J")) }
function HideCursor (line 24) | func HideCursor() { Stdout.Write([]byte("\033[?25l")) }
function UnHideCursor (line 27) | func UnHideCursor() { Stdout.Write([]byte("\033[?25h")) }
function getSize (line 35) | func getSize() (int, int) {
function Width (line 47) | func Width() int {
function Height (line 53) | func Height() int {
FILE: cmd/internal/super/supercow.go
function getNoSaidCow (line 19) | func getNoSaidCow(cow *cowsay.Cow, opts ...cowsay.Option) (string, error) {
function RunSuperCow (line 29) | func RunSuperCow(phrase string, withBold bool, opts ...cowsay.Option) er...
function createBlankSpace (line 74) | func createBlankSpace(balloon string) string {
function maxLen (line 83) | func maxLen(cow []string) int {
type cowLine (line 94) | type cowLine struct
method Len (line 99) | func (c *cowLine) Len() int {
method Slice (line 103) | func (c *cowLine) Slice(i, j int) string {
function makeCowLines (line 110) | func makeCowLines(cow string) []*cowLine {
type renderer (line 127) | type renderer struct
method createFrames (line 168) | func (r *renderer) createFrames(cow *cowsay.Cow, withBold bool) {
method render (line 214) | func (r *renderer) render() {
method posX (line 230) | func (r *renderer) posX(i int) int {
method newWriter (line 245) | func (r *renderer) newWriter(withBold bool) *Writer {
function newRenderer (line 140) | func newRenderer(saidCow, notSaidCow string) *renderer {
constant magic (line 162) | magic = 2
constant span (line 164) | span = 30 * time.Millisecond
constant standup (line 165) | standup = 3 * time.Second
type Writer (line 239) | type Writer struct
method WriteString (line 263) | func (w *Writer) WriteString(s string) (int, error) { return w.dw.Writ...
method Write (line 266) | func (w *Writer) Write(p []byte) (int, error) { return w.dw.Write(p) }
method SetPosx (line 269) | func (w *Writer) SetPosx(x int) { w.mw.SetPosx(x) }
method SetColorSeq (line 272) | func (w *Writer) SetColorSeq(colorSeq int) { w.dw.SetColorSeq(colorSeq) }
method Reset (line 275) | func (w *Writer) Reset() {
method String (line 280) | func (w *Writer) String() string {
FILE: cow.go
type Cow (line 10) | type Cow struct
method Say (line 44) | func (cow *Cow) Say(phrase string) (string, error) {
method Clone (line 55) | func (cow *Cow) Clone(options ...Option) (*Cow, error) {
function New (line 23) | func New(options ...Option) (*Cow, error) {
type Option (line 68) | type Option
function Eyes (line 72) | func Eyes(s string) Option {
function Tongue (line 81) | func Tongue(s string) Option {
function adjustTo2Chars (line 88) | func adjustTo2Chars(s string) string {
function containCows (line 98) | func containCows(target string) (*CowFile, error) {
type NotFound (line 113) | type NotFound struct
method Error (line 119) | func (n *NotFound) Error() string {
function Type (line 124) | func Type(s string) Option {
function Thinking (line 142) | func Thinking() Option {
function Thoughts (line 152) | func Thoughts(thoughts rune) Option {
function Random (line 160) | func Random() Option {
function pickCow (line 171) | func pickCow() (*CowFile, error) {
function BallonWidth (line 188) | func BallonWidth(size uint) Option {
function DisableWordWrap (line 197) | func DisableWordWrap() Option {
FILE: cow_test.go
function TestCow_Clone (line 12) | func TestCow_Clone(t *testing.T) {
function Test_adjustTo2Chars (line 122) | func Test_adjustTo2Chars(t *testing.T) {
function TestNotFound_Error (line 158) | func TestNotFound_Error(t *testing.T) {
FILE: cowsay.go
function init (line 14) | func init() {
function Say (line 19) | func Say(phrase string, options ...Option) (string, error) {
type LocationType (line 28) | type LocationType
constant InBinary (line 32) | InBinary LocationType = iota
constant InDirectory (line 35) | InDirectory
type CowPath (line 39) | type CowPath struct
method Lookup (line 51) | func (c *CowPath) Lookup(target string) (*CowFile, bool) {
type CowFile (line 65) | type CowFile struct
method ReadAll (line 77) | func (c *CowFile) ReadAll() ([]byte, error) {
function Cows (line 88) | func Cows() ([]*CowPath, error) {
function cowsFromCowPath (line 101) | func cowsFromCowPath() ([]*CowPath, error) {
method GetCow (line 132) | func (cow *Cow) GetCow() (string, error) {
FILE: cowsay_test.go
function TestCows (line 16) | func TestCows(t *testing.T) {
function TestCowPath_Lookup (line 94) | func TestCowPath_Lookup(t *testing.T) {
function TestCowFile_ReadAll (line 132) | func TestCowFile_ReadAll(t *testing.T) {
constant defaultSay (line 159) | defaultSay = ` ________
function TestSay (line 168) | func TestSay(t *testing.T) {
FILE: decoration/aurora.go
method writeAsAurora (line 11) | func (w *Writer) writeAsAurora(b []byte) (nn int, err error) {
method writeStringAsAurora (line 37) | func (w *Writer) writeStringAsAurora(s string) (nn int, err error) {
constant freq (line 64) | freq = 0.01
constant m (line 65) | m = math.Pi / 3
constant redPhase (line 67) | redPhase = 0
constant greenPhase (line 68) | greenPhase = 2 * m
constant bluePhase (line 69) | bluePhase = 4 * m
function rgb (line 74) | func rgb(i float64) int64 {
FILE: decoration/bold.go
method writeAsDefaultBold (line 10) | func (w *Writer) writeAsDefaultBold(b []byte) (nn int, err error) {
method writeStringAsDefaultBold (line 30) | func (w *Writer) writeStringAsDefaultBold(s string) (nn int, err error) {
FILE: decoration/decoration.go
type options (line 8) | type options struct
method maybeBold (line 15) | func (o *options) maybeBold() string {
type Option (line 23) | type Option
function WithBold (line 26) | func WithBold() Option {
function WithRainbow (line 33) | func WithRainbow() Option {
function WithAurora (line 40) | func WithAurora(initialSeq int) Option {
type Writer (line 48) | type Writer struct
method SetColorSeq (line 72) | func (w *Writer) SetColorSeq(colorSeq int) {
method Write (line 80) | func (w *Writer) Write(b []byte) (nn int, err error) {
method WriteString (line 96) | func (w *Writer) WriteString(s string) (n int, err error) {
function NewWriter (line 60) | func NewWriter(w io.Writer, opts ...Option) *Writer {
FILE: decoration/rainbow.go
constant red (line 11) | red = iota + 31
constant green (line 12) | green
constant yellow (line 13) | yellow
constant blue (line 14) | blue
constant magenta (line 15) | magenta
constant cyan (line 16) | cyan
method writeAsRainbow (line 21) | func (w *Writer) writeAsRainbow(b []byte) (nn int, err error) {
method writeStringAsRainbow (line 48) | func (w *Writer) writeStringAsRainbow(s string) (nn int, err error) {
FILE: embed.go
function Asset (line 15) | func Asset(path string) ([]byte, error) {
function AssetNames (line 20) | func AssetNames() []string {
function CowsInBinary (line 38) | func CowsInBinary() []string {
FILE: examples/basic/main.go
function main (line 9) | func main() {
function simple (line 17) | func simple() {
function complex (line 29) | func complex() {
FILE: examples/echo-server/main.go
constant limit (line 16) | limit = 1000
function main (line 18) | func main() {
FILE: split.go
function splitPath (line 8) | func splitPath(s string) []string {
FILE: split_windows.go
function splitPath (line 8) | func splitPath(s string) []string {
Condensed preview — 123 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (132K chars).
[
{
"path": ".github/CODEOWNERS",
"chars": 11,
"preview": "* @Code-Hex"
},
{
"path": ".github/FUNDING.yml",
"chars": 17,
"preview": "github: Code-Hex\n"
},
{
"path": ".github/dependabot.yml",
"chars": 205,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"github-actions\"\n directory: \"/\"\n schedule:\n interval: \"daily\"\n -"
},
{
"path": ".github/workflows/lgtm.yml",
"chars": 392,
"preview": "on:\n pull_request_review:\n types:\n - submitted\n\njobs:\n lgtm:\n name: LGTM\n runs-on: ubuntu-latest\n if:"
},
{
"path": ".github/workflows/main.yml",
"chars": 1171,
"preview": "on:\n push:\n branches:\n - \"master\"\n pull_request:\n\njobs:\n test:\n strategy:\n matrix:\n os: [ubunt"
},
{
"path": ".github/workflows/release.yml",
"chars": 884,
"preview": "on:\n push:\n tags:\n - \"v*.*.*\"\n\njobs:\n release:\n name: Release\n runs-on: ubuntu-latest\n steps:\n - n"
},
{
"path": ".gitignore",
"chars": 315,
"preview": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture spe"
},
{
"path": ".goreleaser.yml",
"chars": 2165,
"preview": "builds:\n - id: cowsay\n dir: cmd\n main: ./cowsay/main.go\n binary: cowsay\n env:\n - CGO_ENABLED=0\n ldf"
},
{
"path": "LICENSE",
"chars": 8893,
"preview": "\t\t The Artistic License 2.0\n\n\t Copyright (c) 2000-2006, The Perl Foundation.\n\n Everyone is permitted to cop"
},
{
"path": "Makefile",
"chars": 715,
"preview": ".PHONY: build\nbuild: build/cowsay build/cowthink\n\n.PHONY: build/cowsay\nbuild/cowsay:\n\tCGO_ENABLED=0 cd cmd && go build -"
},
{
"path": "README.md",
"chars": 5221,
"preview": "# Neo Cowsay\n\nNeo Cowsay is written in Go. This cowsay is extended the original cowsay. added fun more options, and you "
},
{
"path": "balloon.go",
"chars": 3259,
"preview": "package cowsay\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\twordwrap \"github.com/Code-Hex/go-wordwrap\"\n\trunewidth \"github.com/mattn/go-"
},
{
"path": "bin/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "cmd/cmd.go",
"chars": 12,
"preview": "package cmd\n"
},
{
"path": "cmd/cowsay/main.go",
"chars": 205,
"preview": "package main\n\nimport (\n\t\"os\"\n\n\t\"github.com/Code-Hex/Neo-cowsay/cmd/v2/internal/cli\"\n)\n\nvar version string\n\nfunc main() {"
},
{
"path": "cmd/cowthink/main.go",
"chars": 204,
"preview": "package main\n\nimport (\n\t\"os\"\n\n\t\"github.com/Code-Hex/Neo-cowsay/cmd/v2/internal/cli\"\n)\n\nvar version string\n\nfunc main() {"
},
{
"path": "cmd/go.mod",
"chars": 448,
"preview": "module github.com/Code-Hex/Neo-cowsay/cmd/v2\n\ngo 1.16\n\nrequire (\n\tgithub.com/Code-Hex/Neo-cowsay/v2 v2.0.3\n\tgithub.com/C"
},
{
"path": "cmd/go.sum",
"chars": 6738,
"preview": "github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=\ngithub.com/Code-Hex/go-wordwrap v"
},
{
"path": "cmd/internal/cli/cli.go",
"chars": 6285,
"preview": "package cli\n\nimport (\n\t\"bufio\"\n\tcryptorand \"crypto/rand\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"math/big\"\n\t\"math/rand\"\n\t\"os\"\n\t"
},
{
"path": "cmd/internal/cli/cli_test.go",
"chars": 3828,
"preview": "package cli\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cm"
},
{
"path": "cmd/internal/screen/buffer.go",
"chars": 2717,
"preview": "package screen\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n)\n\n// buffer is the global screen buffer\n// Its not recommende"
},
{
"path": "cmd/internal/screen/screen.go",
"chars": 1124,
"preview": "package screen\n\nimport (\n\t\"os\"\n\t\"sync\"\n\n\tcolorable \"github.com/mattn/go-colorable\"\n\t\"golang.org/x/crypto/ssh/terminal\"\n)"
},
{
"path": "cmd/internal/super/supercow.go",
"chars": 5794,
"preview": "package super\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/Code-Hex/Neo-cows"
},
{
"path": "cmd/testdata/cowsay/W_option.txt",
"chars": 163,
"preview": " _____ \n/ foo \\\n| bar |\n\\ baz /\n ----- \n \\ ^__^\n \\ (oo)\\_______\n (__)\\ )\\/\\\n "
},
{
"path": "cmd/testdata/cowsay/b_option.txt",
"chars": 165,
"preview": " ___________ \n< foobarbaz >\n ----------- \n \\ ^__^\n \\ (==)\\_______\n (__)\\ )\\/\\\n "
},
{
"path": "cmd/testdata/cowsay/d_option.txt",
"chars": 168,
"preview": " ____________ \n< 0xdeadbeef >\n ------------ \n \\ ^__^\n \\ (xx)\\_______\n (__)\\ )\\/\\\n "
},
{
"path": "cmd/testdata/cowsay/eyes_option.txt",
"chars": 177,
"preview": " _______________ \n< I'm not angry >\n --------------- \n \\ ^__^\n \\ (^^)\\_______\n (__)\\ "
},
{
"path": "cmd/testdata/cowsay/f_tux_option.txt",
"chars": 173,
"preview": " ________________ \n< what is macOS? >\n ---------------- \n \\\n \\\n .--.\n |o_o |\n |:_/ |\n // "
},
{
"path": "cmd/testdata/cowsay/g_option.txt",
"chars": 177,
"preview": " _______________ \n< give me money >\n --------------- \n \\ ^__^\n \\ ($$)\\_______\n (__)\\ "
},
{
"path": "cmd/testdata/cowsay/n_option.txt",
"chars": 163,
"preview": " _____ \n/ foo \\\n| bar |\n\\ baz /\n ----- \n \\ ^__^\n \\ (oo)\\_______\n (__)\\ )\\/\\\n "
},
{
"path": "cmd/testdata/cowsay/p_option.txt",
"chars": 189,
"preview": " ___________________ \n< everyone hates me >\n ------------------- \n \\ ^__^\n \\ (@@)\\_______\n "
},
{
"path": "cmd/testdata/cowsay/s_option.txt",
"chars": 174,
"preview": " ______________ \n< I don't know >\n -------------- \n \\ ^__^\n \\ (**)\\_______\n (__)\\ )\\/"
},
{
"path": "cmd/testdata/cowsay/t_option.txt",
"chars": 153,
"preview": " _______ \n< tired >\n ------- \n \\ ^__^\n \\ (--)\\_______\n (__)\\ )\\/\\\n ||-"
},
{
"path": "cmd/testdata/cowsay/tongue_option.txt",
"chars": 156,
"preview": " ________ \n< hungry >\n -------- \n \\ ^__^\n \\ (oo)\\_______\n (__)\\ )\\/\\\n : "
},
{
"path": "cmd/testdata/cowsay/wired_option.txt",
"chars": 210,
"preview": " __________________________ \n< Wanna Netflix and chill? >\n -------------------------- \n \\ ^__^\n \\ (OO)"
},
{
"path": "cmd/testdata/cowsay/y_option.txt",
"chars": 204,
"preview": " ________________________ \n< I forgot my ID at home >\n ------------------------ \n \\ ^__^\n \\ (..)\\_____"
},
{
"path": "cmd/testdata/cowthink/W_option.txt",
"chars": 163,
"preview": " _____ \n( foo )\n( bar )\n( baz )\n ----- \n o ^__^\n o (oo)\\_______\n (__)\\ )\\/\\\n "
},
{
"path": "cmd/testdata/cowthink/b_option.txt",
"chars": 165,
"preview": " ___________ \n( foobarbaz )\n ----------- \n o ^__^\n o (==)\\_______\n (__)\\ )\\/\\\n "
},
{
"path": "cmd/testdata/cowthink/d_option.txt",
"chars": 168,
"preview": " ____________ \n( 0xdeadbeef )\n ------------ \n o ^__^\n o (xx)\\_______\n (__)\\ )\\/\\\n "
},
{
"path": "cmd/testdata/cowthink/eyes_option.txt",
"chars": 177,
"preview": " _______________ \n( I'm not angry )\n --------------- \n o ^__^\n o (^^)\\_______\n (__)\\ "
},
{
"path": "cmd/testdata/cowthink/f_tux_option.txt",
"chars": 173,
"preview": " ________________ \n( what is macOS? )\n ---------------- \n o\n o\n .--.\n |o_o |\n |:_/ |\n // "
},
{
"path": "cmd/testdata/cowthink/g_option.txt",
"chars": 177,
"preview": " _______________ \n( give me money )\n --------------- \n o ^__^\n o ($$)\\_______\n (__)\\ "
},
{
"path": "cmd/testdata/cowthink/n_option.txt",
"chars": 163,
"preview": " _____ \n( foo )\n( bar )\n( baz )\n ----- \n o ^__^\n o (oo)\\_______\n (__)\\ )\\/\\\n "
},
{
"path": "cmd/testdata/cowthink/p_option.txt",
"chars": 189,
"preview": " ___________________ \n( everyone hates me )\n ------------------- \n o ^__^\n o (@@)\\_______\n "
},
{
"path": "cmd/testdata/cowthink/s_option.txt",
"chars": 174,
"preview": " ______________ \n( I don't know )\n -------------- \n o ^__^\n o (**)\\_______\n (__)\\ )\\/"
},
{
"path": "cmd/testdata/cowthink/t_option.txt",
"chars": 153,
"preview": " _______ \n( tired )\n ------- \n o ^__^\n o (--)\\_______\n (__)\\ )\\/\\\n ||-"
},
{
"path": "cmd/testdata/cowthink/tongue_option.txt",
"chars": 156,
"preview": " ________ \n( hungry )\n -------- \n o ^__^\n o (oo)\\_______\n (__)\\ )\\/\\\n : "
},
{
"path": "cmd/testdata/cowthink/wired_option.txt",
"chars": 210,
"preview": " __________________________ \n( Wanna Netflix and chill? )\n -------------------------- \n o ^__^\n o (OO)"
},
{
"path": "cmd/testdata/cowthink/y_option.txt",
"chars": 204,
"preview": " ________________________ \n( I forgot my ID at home )\n ------------------------ \n o ^__^\n o (..)\\_____"
},
{
"path": "cow.go",
"chars": 3788,
"preview": "package cowsay\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strings\"\n)\n\n// Cow struct!!\ntype Cow struct {\n\teyes string\n\tto"
},
{
"path": "cow_test.go",
"chars": 2809,
"preview": "package cowsay\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmp"
},
{
"path": "cows/beavis.zen.cow",
"chars": 584,
"preview": "##\n## Beavis, with Zen philosophy removed.\n##\n$the_cow = <<EOC;\n $thoughts __------~~-,\n $thoughts ,' "
},
{
"path": "cows/bong.cow",
"chars": 286,
"preview": "##\n## A cow with a bong, from lars@csua.berkeley.edu\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n "
},
{
"path": "cows/bud-frogs.cow",
"chars": 310,
"preview": "##\n## The Budweiser frogs\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n oO)-. .-(O"
},
{
"path": "cows/bunny.cow",
"chars": 123,
"preview": "##\n## A cute little wabbit\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts \\\\\n \\\\ /\\\\\n ( )\n .( o ).\nE"
},
{
"path": "cows/cheese.cow",
"chars": 480,
"preview": "##\n## The cheese from milk & cheese\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n _____ _________\n / \\"
},
{
"path": "cows/cower.cow",
"chars": 230,
"preview": "##\n## A cowering cow\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n ,__, | | \n (oo)\\\\| |___\n "
},
{
"path": "cows/daemon.cow",
"chars": 569,
"preview": "##\n## 4.4 >> 5.4\n##\n$the_cow = <<EOC;\n $thoughts , ,\n $thoughts /( )`\n $thoughts "
},
{
"path": "cows/default.cow",
"chars": 173,
"preview": "$the_cow = <<EOC;\n $thoughts ^__^\n $thoughts ($eyes)\\\\_______\n (__)\\\\ )\\\\/\\\\\n "
},
{
"path": "cows/deno.cow",
"chars": 272,
"preview": "## https://github.com/kawarimidoll/deno-cli-tools/blob/30107adad1dd7d00da8be08c76f843bbfd88b455/denosay/mod.ts#L64-L73\n$"
},
{
"path": "cows/docker.cow",
"chars": 344,
"preview": "##\n## Docker Cow\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n $thoughts\n ## .\n "
},
{
"path": "cows/dragon-and-cow.cow",
"chars": 1284,
"preview": "##\n## A dragon smiting a cow, possible credit to kube@csua.berkeley.edu\n##\n$the_cow = <<EOC;\n $tho"
},
{
"path": "cows/dragon.cow",
"chars": 1000,
"preview": "##\n## The Whitespace Dragon\n##\n$the_cow = <<EOC;\n $thoughts / \\\\ //\\\\\n $thoughts |\\\\__"
},
{
"path": "cows/elephant-in-snake.cow",
"chars": 295,
"preview": "##\n## Do we need to explain this?\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts .... \n .."
},
{
"path": "cows/elephant.cow",
"chars": 284,
"preview": "##\n## An elephant out and about\n##\n$the_cow = <<EOC;\n $thoughts /\\\\ ___ /\\\\\n $thoughts // \\\\/ \\\\/ \\\\\\\\\n ("
},
{
"path": "cows/eyes.cow",
"chars": 585,
"preview": "##\n## Evil-looking eyes\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n .::!!!!!!!:"
},
{
"path": "cows/flaming-sheep.cow",
"chars": 490,
"preview": "##\n## The flaming sheep, contributed by Geordan Rosario (geordan@csua.berkeley.edu)\n##\n$the_cow = <<EOC;\n $thoughts "
},
{
"path": "cows/ghostbusters.cow",
"chars": 1018,
"preview": "##\n## Ghostbusters!\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n $thoughts __---__"
},
{
"path": "cows/gopher.cow",
"chars": 441,
"preview": "$the_cow = <<EOC;\n $thoughts \n $thoughts ,_---~~~~~----._ \n _,,_,*^____ _____``*g*\\\\\"*, \n / __/ "
},
{
"path": "cows/head-in.cow",
"chars": 257,
"preview": "##\n## Go stick yer head in a cow.\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n ^__^ /\n ($eyes)\\\\____"
},
{
"path": "cows/hellokitty.cow",
"chars": 126,
"preview": "##\n## Hello Kitty\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n /\\\\_)o<\n | \\\\\n | O . O|\n \\\\_____"
},
{
"path": "cows/kiss.cow",
"chars": 637,
"preview": "##\n## A lovers' empbrace\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n ,;;;;;;;,\n ;;;;;;;;"
},
{
"path": "cows/kitty.cow",
"chars": 296,
"preview": "##\n## A kitten of sorts, I think\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n (\"`-' '-/\") .___..--' ' \"`-"
},
{
"path": "cows/koala.cow",
"chars": 162,
"preview": "##\n## From the canonical koala collection\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n ___ \n {~._.~}\n "
},
{
"path": "cows/kosh.cow",
"chars": 406,
"preview": "##\n## It's a Kosh Cow!\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n $thoughts\n ___ _____ ___\n / "
},
{
"path": "cows/luke-koala.cow",
"chars": 225,
"preview": "##\n## From the canonical koala collection\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts .\n ___ //\n "
},
{
"path": "cows/meow.cow",
"chars": 473,
"preview": "##\n## A meowing tiger?\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts , _ ___.--'''`--''//-,-_--_.\n \\\\`\"' ` || \\\\\\"
},
{
"path": "cows/milk.cow",
"chars": 439,
"preview": "##\n## Milk from Milk and Cheese\n##\n$the_cow = <<EOC;\n $thoughts ____________ \n $thoughts |__________|\n / "
},
{
"path": "cows/moofasa.cow",
"chars": 245,
"preview": "##\n## MOOfasa.\n##\n$the_cow = <<EOC;\n $thoughts ____\n $thoughts / \\\\\n | ^__^ |\n | ("
},
{
"path": "cows/moose.cow",
"chars": 203,
"preview": "$the_cow = <<EOC;\n $thoughts\n $thoughts \\\\_\\\\_ _/_/\n $thoughts \\\\__/\n ($eyes)\\\\_______\n "
},
{
"path": "cows/mutilated.cow",
"chars": 201,
"preview": "##\n## A mutilated cow, from aspolito@csua.berkeley.edu\n##\n$the_cow = <<EOC;\n $thoughts \\\\_______\n v__v $though"
},
{
"path": "cows/ren.cow",
"chars": 252,
"preview": "##\n## Ren \n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n ____ \n /# /_\\\\_\n | |/o\\\\o\\\\\n | \\\\\\\\_/_/\n / |_ | "
},
{
"path": "cows/sage.cow",
"chars": 290,
"preview": "## http://www.chris.com/ascii/joan/www.geocities.com/SoHo/7373/men.html#confucious\n$the_cow = <<EOC;\n $thoughts\n $t"
},
{
"path": "cows/satanic.cow",
"chars": 186,
"preview": "##\n## Satanic cow, source unknown.\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts (__) \n (\\\\/) \n /-----"
},
{
"path": "cows/sheep.cow",
"chars": 234,
"preview": "##\n## The non-flaming sheep.\n##\n$the_cow = <<EOC\n $thoughts\n $thoughts\n __ \n U${eyes}U\\\\.'\\@\\@\\@\\@\\@\\@"
},
{
"path": "cows/skeleton.cow",
"chars": 433,
"preview": "##\n## This 'Scowleton' brought to you by one of \n## {appel,kube,rowe}@csua.berkeley.edu\n##\n$the_cow = <<EOC;\n $"
},
{
"path": "cows/small.cow",
"chars": 165,
"preview": "##\n## A small cow, artist unknown\n##\n$the_cow = <<EOC;\n $thoughts ,__,\n $thoughts ($eyes)____\n "
},
{
"path": "cows/sodomized.cow",
"chars": 317,
"preview": "##\n## A cow performing an unnatural act, artist unknown.\n##\n$the_cow = <<EOC;\n $thoughts _\n $t"
},
{
"path": "cows/squirrel.cow",
"chars": 283,
"preview": "$the_cow = <<EOC;\n $thoughts\n $thoughts\n _ _\n | \\__/| .~ ~.\n /$eyes `./ .'\n "
},
{
"path": "cows/stegosaurus.cow",
"chars": 854,
"preview": "##\n## A stegosaur with a top hat?\n##\n$the_cow = <<EOC;\n $thoughts . .\n $thoughts "
},
{
"path": "cows/stimpy.cow",
"chars": 364,
"preview": "##\n## Stimpy!\n##\n$the_cow = <<EOC;\n $thoughts . _ . \n $thoughts |\\\\_|/__/| \n / / \\\\/ \\\\ \\\\ \n"
},
{
"path": "cows/supermilker.cow",
"chars": 280,
"preview": "##\n## A cow being milked, probably from Lars Smith (lars@csua.berkeley.edu)\n##\n$the_cow = <<EOC;\n $thoughts ^__^\n $"
},
{
"path": "cows/surgery.cow",
"chars": 892,
"preview": "##\n## A cow operation, artist unknown\n##\n$the_cow = <<EOC;\n $thoughts \\\\ / \n $thoughts "
},
{
"path": "cows/telebears.cow",
"chars": 333,
"preview": "##\n## A cow performing an unnatural act, artist unknown.\n##\n$the_cow = <<EOC;\n $thoughts _\n $t"
},
{
"path": "cows/turkey.cow",
"chars": 1302,
"preview": "##\n## Turkey!\n##\n$the_cow = <<EOC;\n $thoughts ,+*^^*+___+++_\n $thoughts "
},
{
"path": "cows/turtle.cow",
"chars": 1105,
"preview": "##\n## A mysterious turtle...\n##\n$the_cow = <<EOC;\n $thoughts ___-------___\n $tho"
},
{
"path": "cows/tux.cow",
"chars": 215,
"preview": "##\n## TuX\n## (c) pborys@p-soft.silesia.linux.org.pl \n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts\n .--.\n "
},
{
"path": "cows/vader-koala.cow",
"chars": 213,
"preview": "##\n## Another canonical koala?\n##\n$the_cow = <<EOC;\n $thoughts\n $thoughts .\n .---. //\n Y|o o|Y// \n "
},
{
"path": "cows/vader.cow",
"chars": 279,
"preview": "##\n## Cowth Vader, from geordan@csua.berkeley.edu\n##\n$the_cow = <<EOC;\n $thoughts ,-^-.\n $thoughts !"
},
{
"path": "cows/www.cow",
"chars": 248,
"preview": "##\n## A cow wadvertising the World Wide Web, from lim@csua.berkeley.edu\n##\n$the_cow = <<EOC;\n $thoughts ^__^\n "
},
{
"path": "cowsay.go",
"chars": 3737,
"preview": "package cowsay\n\nimport (\n\t\"io/ioutil\"\n\t\"math/rand\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc ini"
},
{
"path": "cowsay_test.go",
"chars": 4728,
"preview": "package cowsay\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/goog"
},
{
"path": "decoration/aurora.go",
"chars": 1675,
"preview": "package decoration\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n)\n\nfunc (w *Writer) writeAsAurora(b []byte)"
},
{
"path": "decoration/bold.go",
"chars": 903,
"preview": "package decoration\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n)\n\nfunc (w *Writer) writeAsDefaultBold(b []byte) (n"
},
{
"path": "decoration/decoration.go",
"chars": 2211,
"preview": "package decoration\n\nimport (\n\t\"bytes\"\n\t\"io\"\n)\n\ntype options struct {\n\twithBold bool\n\twithRainbow bool\n\twithAurora bo"
},
{
"path": "decoration/rainbow.go",
"chars": 1291,
"preview": "package decoration\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n)\n\nconst (\n\tred = iota + 31\n\tgreen\n\tyellow\n\tblue\n\tm"
},
{
"path": "doc/cowsay.1",
"chars": 5545,
"preview": "'\\\" t\n.\\\" Title: cowsay\n.\\\" Author: [see the \"AUTHOR(S)\" section]\n.\\\" Generator: Asciidoctor 2.0.16\n.\\\" Date"
},
{
"path": "doc/cowsay.1.txt.tpl",
"chars": 4653,
"preview": "cowsay(1)\n=========\n\nName\n----\nNeo cowsay/cowthink - configurable speaking/thinking cow (and a bit more)\n\nSYNOPSIS\n-----"
},
{
"path": "embed.go",
"chars": 847,
"preview": "package cowsay\n\nimport (\n\t\"embed\"\n\t\"sort\"\n\t\"strings\"\n)\n\n//go:embed cows/*\nvar cowsDir embed.FS\n\n// Asset loads and retur"
},
{
"path": "examples/basic/go.mod",
"chars": 161,
"preview": "module github.com/Code-Hex/Neo-cowsay/v2/example\n\ngo 1.16\n\nrequire github.com/Code-Hex/Neo-cowsay/v2 v2.0.1\n\nreplace git"
},
{
"path": "examples/basic/go.sum",
"chars": 1108,
"preview": "github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=\ngithub.com/Code-Hex/go-wordwrap v"
},
{
"path": "examples/basic/main.go",
"chars": 533,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\n\tcowsay \"github.com/Code-Hex/Neo-cowsay/v2\"\n)\n\nfunc main() {\n\tif false {\n\t\tsimple()\n\t} el"
},
{
"path": "examples/echo-server/README.md",
"chars": 418,
"preview": "## echo server\n\n```\n$ go build\n$ ./echo-server\n2021/11/14 16:16:27 server addr => 127.0.0.1:54456\n```\n\n\n## client\n\nUse ["
},
{
"path": "examples/echo-server/go.mod",
"chars": 174,
"preview": "module github.com/Code-Hex/Neo-cowsay/v2/examples/echo-server\n\ngo 1.16\n\nrequire github.com/Code-Hex/Neo-cowsay/v2 v2.0.1"
},
{
"path": "examples/echo-server/go.sum",
"chars": 1108,
"preview": "github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=\ngithub.com/Code-Hex/go-wordwrap v"
},
{
"path": "examples/echo-server/main.go",
"chars": 1008,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strings\"\n\t\"time\"\n\n\tcowsay \"github.com/Code-He"
},
{
"path": "go.mod",
"chars": 244,
"preview": "module github.com/Code-Hex/Neo-cowsay/v2\n\nrequire (\n\tgithub.com/Code-Hex/go-wordwrap v1.0.0\n\tgithub.com/google/go-cmp v0"
},
{
"path": "go.sum",
"chars": 1108,
"preview": "github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=\ngithub.com/Code-Hex/go-wordwrap v"
},
{
"path": "split.go",
"chars": 142,
"preview": "//go:build !windows\n// +build !windows\n\npackage cowsay\n\nimport \"strings\"\n\nfunc splitPath(s string) []string {\n\treturn st"
},
{
"path": "split_windows.go",
"chars": 140,
"preview": "//go:build windows\n// +build windows\n\npackage cowsay\n\nimport \"strings\"\n\nfunc splitPath(s string) []string {\n\treturn stri"
},
{
"path": "testdata/default.cow",
"chars": 155,
"preview": " ________ \n< hello! >\n -------- \n \\ ^__^\n \\ (oo)\\_______\n (__)\\ )\\/\\\n "
},
{
"path": "testdata/nest.cow",
"chars": 452,
"preview": " ______________________________ \n/ ________ \\\n| < cowsay > |\n| -------- "
},
{
"path": "testdata/testdir/test.cow",
"chars": 173,
"preview": "$the_cow = <<EOC;\n $thoughts ^__^\n $thoughts ($eyes)\\\\_______\n (__)\\\\ )\\\\/\\\\\n "
}
]
About this extraction
This page contains the full source code of the Code-Hex/Neo-cowsay GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 123 files (112.2 KB), approximately 39.9k tokens, and a symbol index with 145 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.