Showing preview only (691K chars total). Download the full file or copy to clipboard to get everything.
Repository: NeowayLabs/nash
Branch: master
Commit: a227041ffd50
Files: 174
Total size: 642.6 KB
Directory structure:
gitextract_5jvfgdox/
├── .gitignore
├── .travis.yml
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── _disabled_appveyor.yml
├── ast/
│ ├── doc_test.go
│ ├── node.go
│ ├── node_args.go
│ ├── node_fmt.go
│ ├── node_fmt_test.go
│ ├── nodetype_string.go
│ ├── tree.go
│ └── tree_test.go
├── cmd/
│ ├── nash/
│ │ ├── cli.go
│ │ ├── completer.go
│ │ ├── env.go
│ │ ├── env_test.go
│ │ ├── example.sh
│ │ ├── install.go
│ │ ├── install_test.go
│ │ ├── main.go
│ │ ├── rpc.go
│ │ ├── vendor/
│ │ │ └── github.com/
│ │ │ └── chzyer/
│ │ │ └── readline/
│ │ │ ├── .travis.yml
│ │ │ ├── CHANGELOG.md
│ │ │ ├── LICENSE
│ │ │ ├── README.md
│ │ │ ├── ansi_windows.go
│ │ │ ├── complete.go
│ │ │ ├── complete_helper.go
│ │ │ ├── complete_segment.go
│ │ │ ├── complete_segment_test.go
│ │ │ ├── doc/
│ │ │ │ └── shortcut.md
│ │ │ ├── example/
│ │ │ │ ├── readline-demo/
│ │ │ │ │ └── readline-demo.go
│ │ │ │ ├── readline-im/
│ │ │ │ │ ├── README.md
│ │ │ │ │ └── readline-im.go
│ │ │ │ ├── readline-multiline/
│ │ │ │ │ └── readline-multiline.go
│ │ │ │ ├── readline-pass-strength/
│ │ │ │ │ └── readline-pass-strength.go
│ │ │ │ └── readline-remote/
│ │ │ │ ├── readline-remote-client/
│ │ │ │ │ └── client.go
│ │ │ │ └── readline-remote-server/
│ │ │ │ └── server.go
│ │ │ ├── history.go
│ │ │ ├── operation.go
│ │ │ ├── password.go
│ │ │ ├── rawreader_windows.go
│ │ │ ├── readline.go
│ │ │ ├── readline_test.go
│ │ │ ├── remote.go
│ │ │ ├── runebuf.go
│ │ │ ├── runes/
│ │ │ │ ├── runes.go
│ │ │ │ └── runes_test.go
│ │ │ ├── runes.go
│ │ │ ├── runes_test.go
│ │ │ ├── std.go
│ │ │ ├── std_windows.go
│ │ │ ├── term.go
│ │ │ ├── term_bsd.go
│ │ │ ├── term_linux.go
│ │ │ ├── term_windows.go
│ │ │ ├── terminal.go
│ │ │ ├── utils.go
│ │ │ ├── utils_test.go
│ │ │ ├── utils_unix.go
│ │ │ ├── utils_windows.go
│ │ │ ├── vim.go
│ │ │ └── windows_api.go
│ │ └── vendor.sh
│ └── nashfmt/
│ └── main.go
├── docs/
│ ├── interactive.md
│ ├── reference.md
│ └── stdlib/
│ └── fmt.md
├── errors/
│ └── error.go
├── examples/
│ ├── append.sh
│ ├── args.sh
│ ├── init
│ └── len.sh
├── examples_test.go
├── fuzz.go
├── hack/
│ ├── check.sh
│ ├── install/
│ │ └── unix.sh
│ └── releaser.sh
├── internal/
│ ├── sh/
│ │ ├── builtin/
│ │ │ ├── append.go
│ │ │ ├── append_test.go
│ │ │ ├── chdir.go
│ │ │ ├── doc.go
│ │ │ ├── exec_test.go
│ │ │ ├── exit.go
│ │ │ ├── exit_test.go
│ │ │ ├── format.go
│ │ │ ├── format_test.go
│ │ │ ├── glob.go
│ │ │ ├── glob_test.go
│ │ │ ├── len.go
│ │ │ ├── len_test.go
│ │ │ ├── loader.go
│ │ │ ├── print.go
│ │ │ ├── print_test.go
│ │ │ ├── split.go
│ │ │ ├── split_test.go
│ │ │ └── testdata/
│ │ │ ├── exit.sh
│ │ │ ├── split.sh
│ │ │ └── splitfunc.sh
│ │ ├── builtin.go
│ │ ├── cmd.go
│ │ ├── cmd_test.go
│ │ ├── fncall.go
│ │ ├── fndef.go
│ │ ├── functions_test.go
│ │ ├── internal/
│ │ │ └── fixture/
│ │ │ └── fixture.go
│ │ ├── ioutils_test.go
│ │ ├── log.go
│ │ ├── rfork.go
│ │ ├── rfork_linux.go
│ │ ├── rfork_linux_test.go
│ │ ├── rfork_plan9.go
│ │ ├── shell.go
│ │ ├── shell_import_test.go
│ │ ├── shell_linux_test.go
│ │ ├── shell_regression_test.go
│ │ ├── shell_test.go
│ │ ├── shell_var_test.go
│ │ ├── util.go
│ │ └── util_test.go
│ └── testing/
│ └── fixture/
│ └── io.go
├── nash.go
├── nash_test.go
├── parser/
│ ├── parse.go
│ ├── parse_fmt_test.go
│ ├── parse_regression_test.go
│ └── parse_test.go
├── proposal/
│ ├── 1-scope-management.md
│ └── 2-concurrency.md
├── scanner/
│ ├── examples_test.go
│ ├── lex.go
│ ├── lex_regression_test.go
│ └── lex_test.go
├── sh/
│ ├── obj.go
│ ├── objtype_string.go
│ └── shell.go
├── spec.ebnf
├── spec_test.go
├── stdbin/
│ ├── mkdir/
│ │ ├── main.go
│ │ └── mkdir_test.go
│ ├── pwd/
│ │ └── main.go
│ ├── strings/
│ │ ├── main.go
│ │ ├── strings.go
│ │ └── strings_test.go
│ └── write/
│ ├── common_test.sh
│ ├── fd.go
│ ├── fd_windows.go
│ ├── main.go
│ ├── write.go
│ ├── write_linux_test.sh
│ └── write_test.sh
├── stdlib/
│ ├── io.sh
│ ├── io_example.sh
│ ├── io_test.sh
│ ├── map.sh
│ └── map_test.sh
├── testfiles/
│ ├── ex1.sh
│ ├── fibonacci.sh
│ └── sieve.sh
├── tests/
│ ├── cfg.go
│ ├── doc.go
│ ├── internal/
│ │ ├── assert/
│ │ │ ├── doc.go
│ │ │ ├── equal.go
│ │ │ └── error.go
│ │ ├── sh/
│ │ │ └── shell.go
│ │ └── tester/
│ │ └── tester.go
│ ├── listindex_test.go
│ └── stringindex_test.go
├── token/
│ └── token.go
└── vendor/
└── golang.org/
└── x/
└── exp/
└── ebnf/
├── ebnf.go
└── parser.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
./cmd/cnt/cnt
/cmd/nash/nash
/coverage.txt
cmd/nashfmt/nashfmt
dist
*.exe
stdbin/mkdir/mkdir
stdbin/pwd/pwd
stdbin/strings/strings
stdbin/write/write
coverage.html
================================================
FILE: .travis.yml
================================================
os:
- linux
language: go
sudo: false
go:
- "tip"
- "1.12"
install:
- go get -v golang.org/x/exp/ebnf
- make build
script:
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
- mkdir $HOME/nashroot
- make test
- make build
- ./cmd/nash/nash ./hack/releaser.sh testci
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/52ad02845e880cdca2cf
on_success: change
on_failure: always
on_start: never
email:
- tiago.natel@neoway.com.br
- tiagokatcipis@gmail.com
================================================
FILE: Dockerfile
================================================
FROM golang:1.12
ADD . /go/src/github.com/madlambda/nash
ENV NASHPATH /nashpath
ENV NASHROOT /nashroot
RUN cd /go/src/github.com/madlambda/nash && \
make install
CMD ["/nashroot/bin/nash"]
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2016 Neoway Business Solution
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Makefile
================================================
ifndef version
version=$(shell git rev-list -1 HEAD)
endif
buildargs = -ldflags "-X main.VersionString=$(version)" -v
all: build test install
build:
cd cmd/nash && go build $(buildargs)
cd cmd/nashfmt && go build $(buildargs)
cd stdbin/mkdir && go build $(buildargs)
cd stdbin/pwd && go build $(buildargs)
cd stdbin/write && go build $(buildargs)
cd stdbin/strings && go build $(buildargs)
NASHPATH?=$(HOME)/nash
NASHROOT?=$(HOME)/nashroot
# FIXME: binaries install do not work on windows this way (the .exe)
install: build
@echo
@echo "Installing nash at: "$(NASHROOT)
mkdir -p $(NASHROOT)/bin
rm -f $(NASHROOT)/bin/nash
rm -f $(NASHROOT)/bin/nashfmt
cp -p ./cmd/nash/nash $(NASHROOT)/bin
cp -p ./cmd/nashfmt/nashfmt $(NASHROOT)/bin
rm -rf $(NASHROOT)/stdlib
cp -pr ./stdlib $(NASHROOT)/stdlib
cp -pr ./stdbin/mkdir/mkdir $(NASHROOT)/bin/mkdir
cp -pr ./stdbin/pwd/pwd $(NASHROOT)/bin/pwd
cp -pr ./stdbin/write/write $(NASHROOT)/bin/write
cp -pr ./stdbin/strings/strings $(NASHROOT)/bin/strings
docsdeps:
go get github.com/madlambda/mdtoc/cmd/mdtoc
docs: docsdeps
mdtoc -w ./README.md
mdtoc -w ./docs/interactive.md
mdtoc -w ./docs/reference.md
mdtoc -w ./docs/stdlib/fmt.md
test: build
./hack/check.sh
update-vendor:
cd cmd/nash && nash ./vendor.sh
release: clean
./hack/releaser.sh $(version)
coverage-html: test
go tool cover -html=coverage.txt -o coverage.html
@echo "coverage file: coverage.html"
coverage-show: coverage-html
xdg-open coverage.html
clean:
rm -f cmd/nash/nash
rm -f cmd/nashfmt/nashfmt
rm -rf dist
================================================
FILE: README.md
================================================
<!-- mdtocstart -->
# Table of Contents
- [nash](#nash)
- [Show time!](#show-time)
- [Useful stuff](#useful-stuff)
- [Why nash scripts are reliable?](#why-nash-scripts-are-reliable)
- [Installation](#installation)
- [Release](#release)
- [Linux](#linux)
- [Master](#master)
- [Getting started](#getting-started)
- [Accessing command line args](#accessing-command-line-args)
- [Namespace features](#namespace-features)
- [OK, but how scripts should look like?](#ok-but-how-scripts-should-look-like)
- [Didn't work?](#didnt-work)
- [Language specification](#language-specification)
- [Some Bash comparisons](#some-bash-comparisons)
- [Security](#security)
- [Installing libraries](#installing-libraries)
- [Releasing](#releasing)
- [Want to contribute?](#want-to-contribute)
<!-- mdtocend -->
# nash
[](https://gitter.im/madlambda/nash?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://godoc.org/github.com/madlambda/nash)
[](https://travis-ci.org/madlambda/nash) [](https://goreportcard.com/report/github.com/madlambda/nash)
Nash is a system shell, inspired by plan9 `rc`, that makes it easy to create reliable and safe scripts taking advantages of operating systems namespaces (on linux and plan9) in an idiomatic way.
## Useful stuff
- nashfmt: Formats nash code (like gofmt) but no code styling defined yet (see Installation section).
- [nashcomplete](https://github.com/madlambda/nashcomplete): Autocomplete done in nash script.
- [Dotnash](https://github.com/lborguetti/dotnash): Nash profile customizations (e.g: prompt, aliases, etc)
- [nash-mode](https://github.com/tiago4orion/nash-mode.el): Emacs major mode integrated with `nashfmt`.
## Why nash scripts are reliable?
1. Nash aborts at first non-success status of commands;
2. Nash aborts at first unbound variable;
3. It's possible to check the result status of every component of a pipe;
4. **no eval**;
5. Strings are pure strings (no evaluation of variables);
6. No wildcards (globbing) of files; ('rm \*' removes a file called '\*');
- On windows, the terminal does the globbing when in interactive mode.
- On unix there's libs/completions to achieve something similar.
7. No [obscure](http://explainshell.com/) syntax;
8. Support tooling for indent/format and statically analyze the scripts;
## Installation
Nash uses two environment variables: **NASHROOT** to find the standard nash library and **NASHPATH** to find libraries in general (like user's code).
It is important to have two different paths since this will allow you
to upgrade nash (overwrite nash stdlib) without risking lost your code.
If **NASHPATH** is not set, a default of $HOME/nash will be assumed
($HOMEPATH/nash on windows).
If **NASHROOT** is not set, a default of $HOME/nashroot will be assumed
($HOMEPATH/nashroot on windows).
The libraries lookup dir will be $NASHPATH/lib.
The standard library lookup dir will be $NASHROOT/stdlib.
After installing the nash binary will be located at $NASHROOT/bin.
### Installing a Release
Installing is so stupid that we provide small scripts to do it.
If your platform is not supported take a look at the existent ones
and send a MR with the script for your platform.
#### Unix
If you run a unix based machine (Linux, Darwin/OSX, *BSD, etc)
you can use the script below:
Run:
```
./hack/install/unix.sh
```
### Master
Run:
```
make install
```
## Getting started
Nash syntax resembles a common shell:
```
nash
λ> echo "hello world"
hello world
```
Pipes works like borne shell and derivations:
```sh
λ> cat spec.ebnf | wc -l
108
```
Output redirection works like Plan9 rc, but not only for filenames. It
supports output redirection to tcp, udp and unix network protocols
(unix sockets are not supported on windows).
```sh
# stdout to log.out, stderr to log.err
λ> ./daemon >[1] log.out >[2] log.err
# stderr pointing to stdout
λ> ./daemon-logall >[2=1]
# stdout to /dev/null
λ> ./daemon-quiet >[1=]
# stdout and stderr to tcp address
λ> ./daemon >[1] "udp://syslog:6666" >[2=1]
# stdout to unix file
λ> ./daemon >[1] "unix:///tmp/syslog.sock"
```
**For safety, there's no `eval` or `string/tilde expansion` or `command substitution` in Nash.**
To assign command output to a variable exists the '<=' operator. See the example
below:
```sh
var fullpath <= realpath $path | xargs -n echo
echo $fullpath
```
The symbol '<=' redirects the stdout of the command or function invocation in the
right-hand side to the variable name specified.
If you want the command output splited into an array, then you'll need
to store it in a temporary variable and then use the builtin `split` function.
```sh
var out <= find .
var files <= split($out, "\n")
for f in $files {
echo "File: " + $f
}
```
To avoid problems with spaces in variables being passed as
multiple arguments to commands, nash pass the contents of each
variable as a single argument to the command. It works like
enclosing every variable with quotes before executing the command.
Then the following example do the right thing:
```sh
var fullname = "John Nash"
./ci-register --name $fullname --option somevalue
```
On bash you need to enclose the `$fullname` variable in quotes to avoid problems.
Nash syntax does not support shell expansion from strings. There's no way to
do things like the following in nash:
```bash
echo "The date is: $(date +%D)" # DOESNT WORKS!
```
Instead you need to assign each command output to a proper variable and then
concat it with another string when needed (see the [reference docs](./docs/reference.md)).
In the same way, nash doesn't support shell expansion at `if` condition.
For check if a directory exists you must use:
```sh
-test -d $rootfsDir # if you forget '-', the script will be aborted here
# if path not exists
if $status != "0" {
echo "RootFS does not exists."
exit $status
}
```
Nash stops executing the script at first error found and, in the majority of times, it is what
you want (specially for deploys). But Commands have an explicitly
way to bypass such restriction by prepending a dash '-' to the command statement.
For example:
```sh
fn cleanup()
-rm -rf $buildDir
-rm -rf $tmpDir
}
```
The dash '-' works only for operating system commands, other kind of errors are impossible to bypass.
For example, trying to evaluate an unbound variable aborts the program with error.
```sh
λ> echo $PATH
/bin:/sbin:/usr/bin:/usr/local/bin:/home/user/.local/bin:/home/user/bin:/home/user/.gvm/pkgsets/go1.5.3/global/bin:/home/user/projects/3rdparty/plan9port/bin:/home/user/.gvm/gos/go1.5.3/bin
λ> echo $bleh
ERROR: Variable '$bleh' not set
```
Long commands can be split in multiple lines:
```sh
λ> (aws ec2 attach-internet-gateway --internet-gateway-id $igwid
--vpc-id $vpcid)
λ> var instanceId <= (
aws ec2 run-instances
--image-id ami-xxxxxxxx
--count 1
--instance-type t1.micro
--key-name MyKeyPair
--security-groups my-sg
| jq ".Instances[0].InstanceId"
)
λ> echo $instanceId
```
# Accessing command line args
When you run a nash script like:
```
λ> nash ./examples/args.sh --arg value
```
You can get the args using the **ARGS** variable, that is a list:
```
#!/usr/bin/env nash
echo "iterating through the arguments list"
echo ""
for arg in $ARGS {
echo $arg
}
```
# Namespace features
Nash is built with namespace support only on Linux (Plan9 soon). If
you use OSX, BSD or Windows, then the `rfork` keyword will fail.
*The examples below assume you are on a Linux box.*
Below are some facilities for namespace management inside nash.
Make sure you have USER namespaces enabled in your kernel:
```sh
zgrep CONFIG_USER_NS /proc/config.gz
CONFIG_USER_NS=y
```
If it's not enabled you will need root privileges to execute every example below...
Creating a new process in a new USER namespace (u):
```sh
λ> id
uid=1000(user) gid=1000(user) groups=1000(user),98(qubes)
λ> rfork u {
id
}
uid=0(root) gid=0(root) groups=0(root),65534
```
Yes, Linux supports creation of containers by unprivileged users. Tell
this to the customer success of your container-infrastructure-vendor. :-)
The default UID mapping is: Current UID (getuid) => 0 (no
range support). I'll look into more options for this in the future.
Yes, you can create multiple nested user namespaces. But kernel limits
the number of nested user namespace clones to 32.
```sh
λ> rfork u {
echo "inside first container"
id
rfork u {
echo "inside second namespace..."
id
}
}
```
You can verify that other types of namespace still requires root
capabilities, see for PID namespaces (p).
```sh
λ> rfork p {
id
}
ERROR: fork/exec ./nash: operation not permitted
```
The same happens for mount (m), ipc (i) and uts (s) if used without
user namespace (u) flag.
The `c` flag stands for "container" and is an alias for upmnis (all
types of namespaces). If you want another shell (maybe bash) inside
the namespace:
```sh
λ> rfork c {
bash
}
[root@stay-away nash]# id
uid=0(root) gid=0(root) groups=0(root),65534
[root@stay-away nash]# mount -t proc proc /proc
[root@stay-away nash]#
[root@stay-away nash]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 34648 2748 pts/4 Sl 17:32 0:00 -rcd- -addr /tmp/nash.qNQa.sock
root 5 0.0 0.0 16028 3840 pts/4 S 17:32 0:00 /usr/bin/bash
root 23 0.0 0.0 34436 3056 pts/4 R+ 17:34 0:00 ps aux
```
Everything except the `rfork` is like a common shell. Rfork will spawn a
new process with the namespace flags and executes the commands inside
the block on this namespace. It has the form:
```sh
rfork <flags> {
<statements to run inside the container>
}
```
# OK, but how scripts should look like?
See the project [nash-app-example](https://github.com/madlambda/nash-app-example).
# Didn't work?
I've tested in the following environments:
Linux 4.7-rc7
Archlinux
Linux 4.5.5 (amd64)
Archlinux
Linux 4.3.3 (amd64)
Archlinux
Linux 4.1.13 (amd64)
Fedora release 23
Linux 4.1.13 (amd64)
Debian 8
# Language specification
The specification isn't complete yet, but can be found
[here](https://github.com/madlambda/nash/blob/master/spec.ebnf).
The file `spec_test.go` makes sure it is sane.
# Some Bash comparisons
| Bash | Nash | Description |
| --- | --- | --- |
| `GOPATH=/home/user/gopath` | `GOPATH="/home/user/gopath"` | Nash enforces quoted strings |
| `GOPATH="$HOME/gopath"` | `GOPATH=$HOME+"/gopath"` | Nash doesn't do string expansion |
| `export PATH=/usr/bin` | `PATH="/usr/bin"`<br>`setenv PATH` | setenv operates only on valid variables |
| `export` | `showenv` | |
| `ls -la` | `ls -la` | Simple commads are identical |
| `ls -la "$GOPATH"` | `ls -la $GOPATH` | Nash variables shouldn't be enclosed in quotes, because it's default behaviour |
| `./worker 2>log.err 1>log.out` | `./worker >[2] log.err >[1] log.out` | Nash redirection works like plan9 rc |
| `./worker 2>&1` | `./worker >[2=1]` | Redirection map only works for standard file descriptors (0,1,2) |
# Security
The PID 1 of every namespace created by `nash` is the same nash binary reading
commands from the parent shell via unix socket. It allows the parent namespace
(the script that creates the namespace) to issue commands inside the child
namespace. In the current implementation the unix socket communication is not
secure yet.
# Installing libraries
Lets say you have a nash library and you want to install it. For example you have
the following:
```
awesome/code.sh
```
And you want to install it so you can write code like this:
```
import awesome/code
code_do_awesome_stuff()
```
All you have to do is run:
```
nash -install ./awesome
```
Or:
```
nash -install /absolute/path/awesome
```
The entire awesome dir (and its subdirs) will be copied where nash
searches for libraries (dependent on environment variables).
This is the recommended way of installing nash libraries (althought
you can do it manually if you want).
Single files can also be installed as packages, for example:
```
nash -install ./awesome/code.sh
```
Will enable you to import like this:
```
import code
```
If there is already a package with the given name it will be
overwritten.
# Releasing
To generate a release basically:
* Generate the release on github
* Clone the generated tag
* Run: ``` make release "version=<version>" ```
Where **<version>** must match the version of the git tag.
# Want to contribute?
Open issues and PR :)
The project is in an early stage, be patient because things can change in the future.
> "What I cannot create, I do not understand."
>
> -- Richard Feynman
================================================
FILE: _disabled_appveyor.yml
================================================
version: "1.0.0.{build}"
platform: x64
clone_folder: "c:\\gopath\\src\\github.com\\madlambda\\nash"
environment:
GOPATH: "c:\\gopath"
install:
- "echo %PATH%"
- "echo %GOPATH%"
- "set PATH=%GOPATH%\\bin;c:\\go\\bin;c:\\MinGW\\bin;%PATH%"
- "go version"
- "go env"
- copy c:\MinGW\bin\mingw32-make.exe c:\MinGW\bin\make.exe
- choco install cygwin
- set PATH=C:\\cygwin64\\bin;%PATH%
build_script:
- make build
- make test
notifications:
- provider: GitHubPullRequest
auth_token:
secure: QuTLyXQp/4bQNeeEe5DLt9NIt/TzmZkn87s6wfOWpELX1L5UJyRCKV8AJitZWgwv
template: "{{#passed}}:white_check_mark:{{/passed}}{{#failed}}:x:{{/failed}} [Build {{&projectName}} {{buildVersion}} {{status}}]({{buildUrl}}) (commit {{commitUrl}} by @{{&commitAuthorUsername}})"
================================================
FILE: ast/doc_test.go
================================================
package ast_test
import (
"fmt"
"github.com/madlambda/nash/ast"
"github.com/madlambda/nash/token"
)
func Example_AssignmentNode() {
one := ast.NewNameNode(token.NewFileInfo(1, 0), "one", nil)
two := ast.NewNameNode(token.NewFileInfo(1, 4), "two", nil)
value1 := ast.NewStringExpr(token.NewFileInfo(1, 8), "1", true)
value2 := ast.NewStringExpr(token.NewFileInfo(1, 10), "2", true)
assign := ast.NewAssignNode(token.NewFileInfo(1, 0),
[]*ast.NameNode{one, two},
[]ast.Expr{value1, value2},
)
fmt.Printf("%s", assign)
// Output: one, two = "1", "2"
}
func Example_AssignmentNode_Single() {
operatingSystems := ast.NewNameNode(token.NewFileInfo(1, 0), "operatingSystems", nil)
values := []ast.Expr{
ast.NewStringExpr(token.NewFileInfo(1, 19), "plan9 from bell labs", true),
ast.NewStringExpr(token.NewFileInfo(2, 19), "unix", true),
ast.NewStringExpr(token.NewFileInfo(3, 19), "linux", true),
ast.NewStringExpr(token.NewFileInfo(4, 19), "oberon", true),
ast.NewStringExpr(token.NewFileInfo(5, 19), "windows", true),
}
list := ast.NewListExpr(token.NewFileInfo(0, 18), values)
assign := ast.NewSingleAssignNode(token.NewFileInfo(1, 0),
operatingSystems,
list,
)
fmt.Printf("%s", assign)
// Output: operatingSystems = (
// "plan9 from bell labs"
// "unix"
// "linux"
// "oberon"
// "windows"
// )
}
================================================
FILE: ast/node.go
================================================
package ast
import (
"errors"
"fmt"
"github.com/madlambda/nash/token"
)
const (
// RedirMapNoValue indicates the pipe has not redirection
RedirMapNoValue = -1
// RedirMapSupress indicates the rhs of map was suppressed
RedirMapSupress = -2
RforkFlags = "umnips"
)
type (
// Node represents nodes in the grammar
Node interface {
Type() NodeType
IsEqual(Node) bool
// Line of node in the file
Line() int
// Column of the node in the file
Column() int
// String representation of the node.
// Note that it could not match the correspondent node in
// the source code.
String() string
}
assignable interface {
names() []*NameNode
setEqSpace(int)
getEqSpace() int
string() (string, bool)
}
egalitarian struct{}
// Expr is the interface of expression nodes.
Expr Node
// NodeType is the types of grammar
NodeType int
// BlockNode is the block
BlockNode struct {
NodeType
token.FileInfo
egalitarian
Nodes []Node
}
// An ImportNode represents the node for an "import" keyword.
ImportNode struct {
NodeType
token.FileInfo
egalitarian
Path *StringExpr // Import path
}
// A SetenvNode represents the node for a "setenv" keyword.
SetenvNode struct {
NodeType
token.FileInfo
egalitarian
Name string
assign Node
}
NameNode struct {
NodeType
token.FileInfo
egalitarian
Ident string
Index Expr
}
// AssignNode is a node for variable assignments
AssignNode struct {
NodeType
token.FileInfo
egalitarian
Names []*NameNode
Values []Expr
eqSpace int
}
// ExecAssignNode represents the node for execution assignment.
ExecAssignNode struct {
NodeType
token.FileInfo
egalitarian
Names []*NameNode
cmd Node
eqSpace int
}
// A CommandNode is a node for commands
CommandNode struct {
NodeType
token.FileInfo
egalitarian
name string
args []Expr
redirs []*RedirectNode
multi bool
}
// PipeNode represents the node for a command pipeline.
PipeNode struct {
NodeType
token.FileInfo
egalitarian
cmds []*CommandNode
multi bool
}
// StringExpr is a string argument
StringExpr struct {
NodeType
token.FileInfo
egalitarian
str string
quoted bool
}
// IntExpr is a integer used at indexing
IntExpr struct {
NodeType
token.FileInfo
egalitarian
val int
}
// ListExpr is a list argument
ListExpr struct {
NodeType
token.FileInfo
egalitarian
List []Expr
IsVariadic bool
}
// ConcatExpr is a concatenation of arguments
ConcatExpr struct {
NodeType
token.FileInfo
egalitarian
concat []Expr
}
// VarExpr is a variable argument
VarExpr struct {
NodeType
token.FileInfo
egalitarian
Name string
IsVariadic bool
}
// IndexExpr is a indexed variable
IndexExpr struct {
NodeType
token.FileInfo
egalitarian
Var *VarExpr
Index Expr
IsVariadic bool
}
// RedirectNode represents the output redirection part of a command
RedirectNode struct {
NodeType
token.FileInfo
egalitarian
rmap RedirMap
location Expr
}
// RforkNode is a builtin node for rfork
RforkNode struct {
NodeType
token.FileInfo
egalitarian
arg *StringExpr
tree *Tree
}
// CommentNode is the node for comments
CommentNode struct {
NodeType
token.FileInfo
egalitarian
val string
}
// RedirMap is the map of file descriptors of the redirection
RedirMap struct {
lfd int
rfd int
}
// IfNode represents the node for the "if" keyword.
IfNode struct {
NodeType
token.FileInfo
egalitarian
lvalue Expr
rvalue Expr
op string
elseIf bool
ifTree *Tree
elseTree *Tree
}
// VarAssignDeclNode is a "var" declaration to assign values
VarAssignDeclNode struct {
NodeType
token.FileInfo
egalitarian
Assign *AssignNode
}
// VarExecAssignDeclNode is a var declaration to assign output of fn/cmd
VarExecAssignDeclNode struct {
NodeType
token.FileInfo
egalitarian
ExecAssign *ExecAssignNode
}
// FnArgNode represents function arguments
FnArgNode struct {
NodeType
token.FileInfo
egalitarian
Name string
IsVariadic bool
}
// A FnDeclNode represents a function declaration.
FnDeclNode struct {
NodeType
token.FileInfo
egalitarian
name string
args []*FnArgNode
tree *Tree
}
// A FnInvNode represents a function invocation statement.
FnInvNode struct {
NodeType
token.FileInfo
egalitarian
name string
args []Expr
}
// A ReturnNode represents the "return" keyword.
ReturnNode struct {
NodeType
token.FileInfo
egalitarian
Returns []Expr
}
// A BindFnNode represents the "bindfn" keyword.
BindFnNode struct {
NodeType
token.FileInfo
egalitarian
name string
cmdname string
}
// A ForNode represents the "for" keyword.
ForNode struct {
NodeType
token.FileInfo
egalitarian
identifier string
inExpr Expr
tree *Tree
}
)
//go:generate stringer -type=NodeType
const (
// NodeSetenv is the type for "setenv" builtin keyword
NodeSetenv NodeType = iota + 1
// NodeBlock represents a program scope.
NodeBlock
// NodeName represents an identifier
NodeName
// NodeAssign is the type for variable assignment
NodeAssign
// NodeExecAssign is the type for command/function assignment
NodeExecAssign
// NodeImport is the type for "import" builtin keyword
NodeImport
execBegin
// NodeCommand is the type for command execution
NodeCommand
// NodePipe is the type for pipeline execution
NodePipe
// NodeRedirect is the type for redirection nodes
NodeRedirect
// NodeFnInv is the type for function invocation
NodeFnInv
execEnd
expressionBegin
// NodeStringExpr is the type of string expression (quoted or not).
NodeStringExpr
// NodeIntExpr is the type of integer expression (commonly list indexing)
NodeIntExpr
// NodeVarExpr is the type of variable expressions.
NodeVarExpr
// NodeListExpr is the type of list expression.
NodeListExpr
// NodeIndexExpr is the type of indexing expressions.
NodeIndexExpr
// NodeConcatExpr is the type of concatenation expressions.
NodeConcatExpr
expressionEnd
// NodeString are nodes for argument strings
NodeString
// NodeRfork is the type for rfork statement
NodeRfork
// NodeRforkFlags are nodes for rfork flags
NodeRforkFlags
// NodeIf is the type for if statements
NodeIf
// NodeComment are nodes for comment
NodeComment
NodeFnArg
// NodeVarAssignDecl is the type for var declaration of values
NodeVarAssignDecl
// NodeVarExecAssignDecl
NodeVarExecAssignDecl
// NodeFnDecl is the type for function declaration
NodeFnDecl
// NodeReturn is the type for return statement
NodeReturn
// NodeBindFn is the type for bindfn statements
NodeBindFn
// NodeFor is the type for "for" statements
NodeFor
)
var (
DebugCmp bool
)
func debug(format string, args ...interface{}) {
if DebugCmp {
fmt.Printf("[debug] "+format+"\n", args...)
}
}
// Type returns the type of the node
func (t NodeType) Type() NodeType {
return t
}
// IsExpr returns if the node is an expression.
func (t NodeType) IsExpr() bool {
return t > expressionBegin && t < expressionEnd
}
// IsExecutable returns if the node is executable
func (t NodeType) IsExecutable() bool {
return t > execBegin && t < execEnd
}
func (e egalitarian) equal(node, other Node) bool {
if node == other {
return true
}
if node == nil {
return false
}
if !cmpInfo(node, other) {
return false
}
return true
}
// NewBlockNode creates a new block
func NewBlockNode(info token.FileInfo) *BlockNode {
return &BlockNode{
NodeType: NodeBlock,
FileInfo: info,
}
}
// Push adds a new node for a block of nodes
func (l *BlockNode) Push(n Node) {
l.Nodes = append(l.Nodes, n)
}
// IsEqual returns if it is equal to the other node.
func (l *BlockNode) IsEqual(other Node) bool {
if !l.equal(l, other) {
return false
}
o, ok := other.(*BlockNode)
if !ok {
debug("Failed to cast other node to BlockNode")
return false
}
if len(l.Nodes) != len(o.Nodes) {
debug("Nodes differs in length")
return false
}
for i := 0; i < len(l.Nodes); i++ {
if !l.Nodes[i].IsEqual(o.Nodes[i]) {
debug("List entry %d differ... '%s' != '%s'", i, l.Nodes[i], o.Nodes[i])
return false
}
}
return true
}
// NewImportNode creates a new ImportNode object
func NewImportNode(info token.FileInfo, path *StringExpr) *ImportNode {
return &ImportNode{
NodeType: NodeImport,
FileInfo: info,
Path: path,
}
}
// IsEqual returns if it is equal to the other node.
func (n *ImportNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*ImportNode)
if !ok {
debug("Failed to cast to ImportNode")
return false
}
if n.Path != o.Path {
if n.Path != nil {
return n.Path.IsEqual(o.Path)
}
}
return false
}
// NewSetenvNode creates a new assignment node
func NewSetenvNode(info token.FileInfo, name string, assign Node) (*SetenvNode, error) {
if assign != nil && assign.Type() != NodeAssign &&
assign.Type() != NodeExecAssign {
return nil, errors.New("Invalid assignment in setenv")
}
return &SetenvNode{
NodeType: NodeSetenv,
FileInfo: info,
Name: name,
assign: assign,
}, nil
}
// Assignment returns the setenv assignment (if any)
func (n *SetenvNode) Assignment() Node { return n.assign }
// IsEqual returns if it is equal to the other node.
func (n *SetenvNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*SetenvNode)
if !ok {
debug("Failed to convert to SetenvNode")
return false
}
if n.assign != o.assign {
if !n.assign.IsEqual(o.assign) {
return false
}
}
return n.Name == o.Name
}
func NewNameNode(info token.FileInfo, ident string, index Expr) *NameNode {
return &NameNode{
NodeType: NodeName,
FileInfo: info,
Ident: ident,
Index: index,
}
}
func (n *NameNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*NameNode)
if !ok {
debug("Failed to convert to NameNode")
return false
}
if n.Ident != o.Ident {
return false
}
if n.Index == o.Index {
return true
}
if n.Index != nil {
return n.Index.IsEqual(o.Index)
}
return false
}
// NewAssignNode creates a new tuple assignment (multiple variable
// assigned in a single statement).
// For single assignment see NewSingleAssignNode.
func NewAssignNode(info token.FileInfo, names []*NameNode, values []Expr) *AssignNode {
return &AssignNode{
NodeType: NodeAssign,
FileInfo: info,
eqSpace: -1,
Names: names,
Values: values,
}
}
// NewSingleAssignNode creates an assignment of a single variable. Eg.:
// name = "hello"
// To make an assignment of multiple variables in the same statement
// use `NewAssignNode`.
func NewSingleAssignNode(info token.FileInfo, name *NameNode, value Expr) *AssignNode {
return NewAssignNode(info, []*NameNode{name}, []Expr{value})
}
// TODO(i4k): fix that
func (n *AssignNode) names() []*NameNode { return n.Names }
func (n *AssignNode) getEqSpace() int { return n.eqSpace }
func (n *AssignNode) setEqSpace(value int) { n.eqSpace = value }
// IsEqual returns if it is equal to the other node.
func (n *AssignNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*AssignNode)
if !ok {
debug("Failed to convert to AssignNode")
return false
}
if len(n.Names) == len(o.Names) {
for i := 0; i < len(n.Names); i++ {
if !n.Names[i].IsEqual(o.Names[i]) {
debug("Assignment identifier doesn't match: '%s' != '%s'",
n.Names[i], o.Names[i])
return false
}
}
} else {
return false
}
if len(n.Values) == len(o.Values) {
for i := 0; i < len(n.Values); i++ {
if !n.Values[i].IsEqual(o.Values[i]) {
return false
}
}
} else {
return false
}
return true
}
// NewExecAssignNode creates a new node for executing something and store the
// result on a new variable. The assignment could be made using an operating system
// command, a pipe of commands or a function invocation.
// It returns a *ExecAssignNode ready to be executed or error when n is not a valid
// node for execution.
// TODO(i4k): Change the API to specific node types. Eg.: NewExecAssignCmdNode and
// so on.
func NewExecAssignNode(info token.FileInfo, names []*NameNode, n Node) (*ExecAssignNode, error) {
if !n.Type().IsExecutable() {
return nil, errors.New("NewExecAssignNode expects a CommandNode, PipeNode or FninvNode")
}
return &ExecAssignNode{
NodeType: NodeExecAssign,
FileInfo: info,
Names: names,
cmd: n,
eqSpace: -1,
}, nil
}
func (n *ExecAssignNode) names() []*NameNode { return n.Names }
func (n *ExecAssignNode) getEqSpace() int { return n.eqSpace }
func (n *ExecAssignNode) setEqSpace(value int) { n.eqSpace = value }
// Command returns the command (or r-value). Command could be a CommandNode or FnNode
func (n *ExecAssignNode) Command() Node {
return n.cmd
}
// SetCommand set the command part (NodeCommand or NodeFnDecl)
func (n *ExecAssignNode) SetCommand(c Node) {
n.cmd = c
}
func (n *ExecAssignNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*ExecAssignNode)
if !ok {
debug("Failed to convert to ExecAssignNode")
return false
}
if len(n.Names) != len(o.Names) {
return false
}
for i := 0; i < len(n.Names); i++ {
if n.Names[i] != nil {
if !n.Names[i].IsEqual(o.Names[i]) {
debug("Exec assignment name differs")
return false
}
}
}
if n.cmd == o.cmd {
return true
} else if n.cmd != nil {
return n.cmd.IsEqual(o.cmd)
}
return false
}
// NewCommandNode creates a new node for commands
func NewCommandNode(info token.FileInfo, name string, multiline bool) *CommandNode {
return &CommandNode{
NodeType: NodeCommand,
FileInfo: info,
name: name,
multi: multiline,
}
}
func (n *CommandNode) IsMulti() bool { return n.multi }
func (n *CommandNode) SetMulti(b bool) { n.multi = b }
// AddArg adds a new argument to the command
func (n *CommandNode) AddArg(a Expr) {
n.args = append(n.args, a)
}
// SetArgs sets an array of args to command
func (n *CommandNode) SetArgs(args []Expr) {
n.args = args
}
// Args returns the list of arguments supplied to command.
func (n *CommandNode) Args() []Expr { return n.args }
// AddRedirect adds a new redirect node to command
func (n *CommandNode) AddRedirect(redir *RedirectNode) {
n.redirs = append(n.redirs, redir)
}
// Redirects return the list of redirect maps of the command.
func (n *CommandNode) Redirects() []*RedirectNode { return n.redirs }
// Name returns the program name
func (n *CommandNode) Name() string { return n.name }
// IsEqual returns if it is equal to the other node.
func (n *CommandNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*CommandNode)
if !ok {
debug("Failed to convert to CommandNode")
return false
}
if n.multi != o.multi {
debug("Command multiline differs.")
return false
}
if len(n.args) != len(o.args) {
debug("Command argument length differs: %d (%+v) != %d (%+v)",
len(n.args), n.args, len(o.args), o.args)
return false
}
for i := 0; i < len(n.args); i++ {
if !n.args[i].IsEqual(o.args[i]) {
debug("Argument %d differs. '%s' != '%s'", i, n.args[i],
o.args[i])
return false
}
}
if len(n.redirs) != len(o.redirs) {
debug("Number of redirects differs. %d != %d", len(n.redirs),
len(o.redirs))
return false
}
for i := 0; i < len(n.redirs); i++ {
if n.redirs[i] == o.redirs[i] {
continue
} else if n.redirs[i] != nil &&
!n.redirs[i].IsEqual(o.redirs[i]) {
debug("Redirect differs... %s != %s", n.redirs[i],
o.redirs[i])
return false
}
}
return n.name == o.name
}
// NewPipeNode creates a new command pipeline
func NewPipeNode(info token.FileInfo, multi bool) *PipeNode {
return &PipeNode{
NodeType: NodePipe,
FileInfo: info,
multi: multi,
}
}
func (n *PipeNode) IsMulti() bool { return n.multi }
func (n *PipeNode) SetMulti(b bool) { n.multi = b }
// AddCmd add another command to end of the pipeline
func (n *PipeNode) AddCmd(c *CommandNode) {
n.cmds = append(n.cmds, c)
}
// Commands returns the list of pipeline commands
func (n *PipeNode) Commands() []*CommandNode {
return n.cmds
}
// IsEqual returns if it is equal to the other node.
func (n *PipeNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*PipeNode)
if !ok {
debug("Failed to convert to PipeNode")
return false
}
if len(n.cmds) != len(o.cmds) {
debug("Number of pipe commands differ: %d != %d",
len(n.cmds), len(o.cmds))
return false
}
for i := 0; i < len(n.cmds); i++ {
if !n.cmds[i].IsEqual(o.cmds[i]) {
debug("Command differs. '%s' != '%s'", n.cmds[i],
o.cmds[i])
return false
}
}
return true
}
// NewRedirectNode creates a new redirection node for commands
func NewRedirectNode(info token.FileInfo) *RedirectNode {
return &RedirectNode{
NodeType: NodeRedirect,
FileInfo: info,
rmap: RedirMap{
lfd: -1,
rfd: -1,
},
}
}
// SetMap sets the redirection map. Eg.: [2=1]
func (r *RedirectNode) SetMap(lfd int, rfd int) {
r.rmap.lfd = lfd
r.rmap.rfd = rfd
}
// LeftFD return the lhs of the redirection map.
func (r *RedirectNode) LeftFD() int { return r.rmap.lfd }
// RightFD return the rhs of the redirection map.
func (r *RedirectNode) RightFD() int { return r.rmap.rfd }
// SetLocation of the output
func (r *RedirectNode) SetLocation(s Expr) { r.location = s }
// Location return the location of the redirection.
func (r *RedirectNode) Location() Expr { return r.location }
// IsEqual return if it is equal to the other node.
func (r *RedirectNode) IsEqual(other Node) bool {
if !r.equal(r, other) {
return false
}
o, ok := other.(*RedirectNode)
if !ok {
return false
}
if r.rmap.lfd != o.rmap.lfd ||
r.rmap.rfd != o.rmap.rfd {
return false
}
if r.location == o.location {
return true
} else if r.location != nil {
return r.location.IsEqual(o.location)
}
return false
}
// NewRforkNode creates a new node for rfork
func NewRforkNode(info token.FileInfo) *RforkNode {
return &RforkNode{
NodeType: NodeRfork,
FileInfo: info,
}
}
// Arg return the string argument of the rfork.
func (n *RforkNode) Arg() *StringExpr {
return n.arg
}
// SetFlags sets the rfork flags
func (n *RforkNode) SetFlags(a *StringExpr) {
n.arg = a
}
// Tree returns the child tree of node
func (n *RforkNode) Tree() *Tree {
return n.tree
}
// SetTree set the body of the rfork block.
func (n *RforkNode) SetTree(t *Tree) {
n.tree = t
}
func (n *RforkNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*RforkNode)
if !ok {
return false
}
if n.arg == o.arg {
return true
}
if n.arg != nil {
if !n.arg.IsEqual(o.arg) {
return false
}
}
return n.tree.IsEqual(o.tree)
}
// NewCommentNode creates a new node for comments
func NewCommentNode(info token.FileInfo, val string) *CommentNode {
return &CommentNode{
NodeType: NodeComment,
FileInfo: info,
val: val,
}
}
func (n *CommentNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
if n.Type() != other.Type() {
return false
}
o, ok := other.(*CommentNode)
if !ok {
return false
}
return n.val == o.val
}
// NewIfNode creates a new if block statement
func NewIfNode(info token.FileInfo) *IfNode {
return &IfNode{
NodeType: NodeIf,
FileInfo: info,
}
}
// Lvalue returns the lefthand part of condition
func (n *IfNode) Lvalue() Expr {
return n.lvalue
}
// Rvalue returns the righthand side of condition
func (n *IfNode) Rvalue() Expr {
return n.rvalue
}
// SetLvalue set the lefthand side of condition
func (n *IfNode) SetLvalue(arg Expr) {
n.lvalue = arg
}
// SetRvalue set the righthand side of condition
func (n *IfNode) SetRvalue(arg Expr) {
n.rvalue = arg
}
// Op returns the condition operation
func (n *IfNode) Op() string { return n.op }
// SetOp set the condition operation
func (n *IfNode) SetOp(op string) {
n.op = op
}
// IsElseIf tells if the if is an else-if statement
func (n *IfNode) IsElseIf() bool {
return n.elseIf
}
// SetElseif sets the else-if part
func (n *IfNode) SetElseif(b bool) {
n.elseIf = b
}
// SetIfTree sets the block of statements of the if block
func (n *IfNode) SetIfTree(t *Tree) {
n.ifTree = t
}
// SetElseTree sets the block of statements of the else block
func (n *IfNode) SetElseTree(t *Tree) {
n.elseTree = t
}
// IfTree returns the if block
func (n *IfNode) IfTree() *Tree { return n.ifTree }
// ElseTree returns the else block
func (n *IfNode) ElseTree() *Tree { return n.elseTree }
// IsEqual returns if it is equal to the other node.
func (n *IfNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*IfNode)
if !ok {
debug("Failed to convert to ifNode")
return false
}
elvalue := n.Lvalue()
ervalue := n.Rvalue()
vlvalue := o.Lvalue()
vrvalue := o.Rvalue()
if !elvalue.IsEqual(vlvalue) {
debug("Lvalue differs: '%s' != '%s'", elvalue, vlvalue)
return false
}
if !ervalue.IsEqual(vrvalue) {
debug("Rvalue differs: '%s' != '%s'", ervalue, vrvalue)
return false
}
if n.Op() != o.Op() {
debug("Operation differs: %s != %s", n.Op(), o.Op())
return false
}
expectedTree := n.IfTree()
valueTree := o.IfTree()
if !expectedTree.IsEqual(valueTree) {
debug("If tree differs: '%s' != '%s'", expectedTree,
valueTree)
return false
}
expectedTree = n.ElseTree()
valueTree = o.ElseTree()
return expectedTree.IsEqual(valueTree)
}
func NewFnArgNode(info token.FileInfo, name string, isVariadic bool) *FnArgNode {
return &FnArgNode{
NodeType: NodeFnArg,
FileInfo: info,
Name: name,
IsVariadic: isVariadic,
}
}
func (a *FnArgNode) IsEqual(other Node) bool {
if !a.equal(a, other) {
return false
}
o, ok := other.(*FnArgNode)
if !ok {
return false
}
if a.Name != o.Name ||
a.IsVariadic != o.IsVariadic {
return false
}
return true
}
func NewVarAssignDecl(info token.FileInfo, assignNode *AssignNode) *VarAssignDeclNode {
return &VarAssignDeclNode{
NodeType: NodeVarAssignDecl,
Assign: assignNode,
}
}
func (n *VarAssignDeclNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*VarAssignDeclNode)
if !ok {
return false
}
return n.Assign.IsEqual(o.Assign)
}
func NewVarExecAssignDecl(info token.FileInfo, assignNode *ExecAssignNode) *VarExecAssignDeclNode {
return &VarExecAssignDeclNode{
NodeType: NodeVarExecAssignDecl,
ExecAssign: assignNode,
}
}
func (n *VarExecAssignDeclNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*VarExecAssignDeclNode)
if !ok {
return false
}
return n.ExecAssign.IsEqual(o.ExecAssign)
}
// NewFnDeclNode creates a new function declaration
func NewFnDeclNode(info token.FileInfo, name string) *FnDeclNode {
return &FnDeclNode{
NodeType: NodeFnDecl,
FileInfo: info,
name: name,
}
}
// SetName set the function name
func (n *FnDeclNode) SetName(a string) {
n.name = a
}
// Name return the function name
func (n *FnDeclNode) Name() string {
return n.name
}
// Args returns function arguments
func (n *FnDeclNode) Args() []*FnArgNode {
return n.args
}
// AddArg add a new argument to end of argument list
func (n *FnDeclNode) AddArg(arg *FnArgNode) {
n.args = append(n.args, arg)
}
// Tree return the function block
func (n *FnDeclNode) Tree() *Tree {
return n.tree
}
// SetTree set the function tree
func (n *FnDeclNode) SetTree(t *Tree) {
n.tree = t
}
func (n *FnDeclNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*FnDeclNode)
if !ok {
return false
}
if n.name != o.name || len(n.args) != len(o.args) {
return false
}
for i := 0; i < len(n.args); i++ {
if !n.args[i].IsEqual(o.args[i]) {
return false
}
}
return true
}
// NewFnInvNode creates a new function invocation
func NewFnInvNode(info token.FileInfo, name string) *FnInvNode {
return &FnInvNode{
NodeType: NodeFnInv,
FileInfo: info,
name: name,
}
}
// SetName set the function name
func (n *FnInvNode) SetName(a string) {
n.name = a
}
// Name return the function name
func (n *FnInvNode) Name() string {
return n.name
}
// AddArg add another argument to end of argument list
func (n *FnInvNode) AddArg(arg Expr) {
n.args = append(n.args, arg)
}
// Args return the invocation arguments.
func (n *FnInvNode) Args() []Expr { return n.args }
// IsEqual returns if it is equal to the other node.
func (n *FnInvNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*FnInvNode)
if !ok {
return false
}
if len(n.args) != len(o.args) {
return false
}
for i := 0; i < len(n.args); i++ {
if !n.args[i].IsEqual(o.args[i]) {
return false
}
}
return true
}
// NewBindFnNode creates a new bindfn statement
func NewBindFnNode(info token.FileInfo, name, cmd string) *BindFnNode {
return &BindFnNode{
NodeType: NodeBindFn,
FileInfo: info,
name: name,
cmdname: cmd,
}
}
// Name return the function name
func (n *BindFnNode) Name() string { return n.name }
// CmdName return the command name
func (n *BindFnNode) CmdName() string { return n.cmdname }
func (n *BindFnNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
o, ok := other.(*BindFnNode)
if !ok {
return false
}
return n.name == o.name && n.cmdname == o.cmdname
}
// NewReturnNode create a return statement
func NewReturnNode(info token.FileInfo) *ReturnNode {
return &ReturnNode{
FileInfo: info,
NodeType: NodeReturn,
}
}
func (n *ReturnNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
if n.Type() != other.Type() {
return false
}
o, ok := other.(*ReturnNode)
if !ok {
return false
}
if len(n.Returns) != len(o.Returns) {
return false
}
for i := 0; i < len(n.Returns); i++ {
arg := n.Returns[i]
oarg := o.Returns[i]
if arg != nil && !arg.IsEqual(oarg) {
return false
}
}
return true
}
// NewForNode create a new for statement
func NewForNode(info token.FileInfo) *ForNode {
return &ForNode{
NodeType: NodeFor,
FileInfo: info,
}
}
// SetIdentifier set the for indentifier
func (n *ForNode) SetIdentifier(a string) {
n.identifier = a
}
// Identifier return the identifier part
func (n *ForNode) Identifier() string { return n.identifier }
// InVar return the "in" variable
func (n *ForNode) InExpr() Expr { return n.inExpr }
// SetInVar set "in" expression
func (n *ForNode) SetInExpr(a Expr) { n.inExpr = a }
// SetTree set the for block of statements
func (n *ForNode) SetTree(a *Tree) {
n.tree = a
}
// Tree return the for block
func (n *ForNode) Tree() *Tree { return n.tree }
func (n *ForNode) IsEqual(other Node) bool {
if !n.equal(n, other) {
return false
}
if n.Type() != other.Type() {
return false
}
o, ok := other.(*ForNode)
if !ok {
return false
}
if n.identifier != o.identifier {
return false
}
if n.inExpr == o.inExpr {
return true
}
if n.inExpr != nil {
return n.inExpr.IsEqual(o.inExpr)
}
return false
}
func cmpInfo(n, other Node) bool {
if n.Line() != other.Line() ||
n.Column() != other.Column() {
debug("file info mismatch on %v (%s): (%d, %d) != (%d, %d)",
n, n.Type(), n.Line(), n.Column(),
other.Line(), other.Column())
return false
}
return true
}
================================================
FILE: ast/node_args.go
================================================
package ast
import (
"fmt"
"github.com/madlambda/nash/scanner"
"github.com/madlambda/nash/token"
)
// ArgFromToken is a helper to get an argument based on the lexer token
func ExprFromToken(val scanner.Token) (Expr, error) {
switch val.Type() {
case token.Arg:
return NewStringExpr(token.NewFileInfo(val.Line(), val.Column()), val.Value(), false), nil
case token.String:
return NewStringExpr(token.NewFileInfo(val.Line(), val.Column()), val.Value(), true), nil
case token.Variable:
return NewVarExpr(token.NewFileInfo(val.Line(), val.Column()), val.Value()), nil
}
return nil, fmt.Errorf("argFromToken doesn't support type %v", val)
}
// NewArgString creates a new string argument
func NewStringExpr(info token.FileInfo, value string, quoted bool) *StringExpr {
return &StringExpr{
NodeType: NodeStringExpr,
FileInfo: info,
str: value,
quoted: quoted,
}
}
// Value returns the argument string value
func (s *StringExpr) Value() string {
return s.str
}
func (s *StringExpr) SetValue(a string) {
s.str = a
}
func (s *StringExpr) IsEqual(other Node) bool {
if !s.equal(s, other) {
return false
}
value, ok := other.(*StringExpr)
if !ok {
return false
}
if s.quoted != value.quoted {
return false
}
return s.str == value.str
}
func NewIntExpr(info token.FileInfo, val int) *IntExpr {
return &IntExpr{
NodeType: NodeIntExpr,
FileInfo: info,
val: val,
}
}
func (i *IntExpr) Value() int { return i.val }
func (i *IntExpr) IsEqual(other Node) bool {
if !i.equal(i, other) {
return false
}
o, ok := other.(*IntExpr)
if !ok {
return false
}
return i.val == o.val
}
func NewListExpr(info token.FileInfo, values []Expr) *ListExpr {
return NewListVariadicExpr(info, values, false)
}
func NewListVariadicExpr(info token.FileInfo, values []Expr, variadic bool) *ListExpr {
return &ListExpr{
NodeType: NodeListExpr,
FileInfo: info,
List: values,
IsVariadic: variadic,
}
}
// PushExpr push an expression to end of the list
func (l *ListExpr) PushExpr(a Expr) {
l.List = append(l.List, a)
}
func (l *ListExpr) IsEqual(other Node) bool {
if !l.equal(l, other) {
return false
}
o, ok := other.(*ListExpr)
if !ok {
return false
}
if len(l.List) != len(o.List) {
return false
}
for i, val := range l.List {
oval := o.List[i]
if !val.IsEqual(oval) {
debug("%v(%s) != %v(%s)", val, val.Type(),
oval, oval.Type())
return false
}
}
return true
}
func NewConcatExpr(info token.FileInfo, parts []Expr) *ConcatExpr {
return &ConcatExpr{
NodeType: NodeConcatExpr,
FileInfo: info,
concat: parts,
}
}
// PushExpr push an expression to end of the concat list
func (c *ConcatExpr) PushExpr(a Expr) {
c.concat = append(c.concat, a)
}
// SetConcatList set the concatenation parts
func (c *ConcatExpr) SetConcat(v []Expr) {
c.concat = v
}
func (c *ConcatExpr) List() []Expr { return c.concat }
func (c *ConcatExpr) IsEqual(other Node) bool {
if !c.equal(c, other) {
return false
}
o, ok := other.(*ConcatExpr)
if !ok {
return false
}
if len(c.concat) != len(o.concat) {
return false
}
for i := 0; i < len(c.concat); i++ {
if !c.concat[i].IsEqual(o.concat[i]) {
return false
}
}
return true
}
func NewVarExpr(info token.FileInfo, name string) *VarExpr {
return NewVarVariadicExpr(info, name, false)
}
func NewVarVariadicExpr(info token.FileInfo, name string, isVariadic bool) *VarExpr {
return &VarExpr{
NodeType: NodeVarExpr,
FileInfo: info,
Name: name,
IsVariadic: isVariadic,
}
}
func (v *VarExpr) IsEqual(other Node) bool {
if !v.equal(v, other) {
return false
}
o, ok := other.(*VarExpr)
if !ok {
return false
}
return v.Name == o.Name &&
v.IsVariadic == o.IsVariadic
}
func NewIndexExpr(info token.FileInfo, va *VarExpr, idx Expr) *IndexExpr {
return NewIndexVariadicExpr(info, va, idx, false)
}
func NewIndexVariadicExpr(info token.FileInfo, va *VarExpr, idx Expr, variadic bool) *IndexExpr {
return &IndexExpr{
NodeType: NodeIndexExpr,
FileInfo: info,
Var: va,
Index: idx,
IsVariadic: variadic,
}
}
func (i *IndexExpr) IsEqual(other Node) bool {
if !i.equal(i, other) {
return false
}
o, ok := other.(*IndexExpr)
if !ok {
return false
}
return i.Var.IsEqual(o.Var) &&
i.Index.IsEqual(o.Index) &&
i.IsVariadic == o.IsVariadic
}
================================================
FILE: ast/node_fmt.go
================================================
package ast
import (
"fmt"
"strconv"
"strings"
)
func (s *StringExpr) String() string {
if s.quoted {
return `"` + stringify(s.str) + `"`
}
return s.str
}
func (i *IntExpr) String() string {
return strconv.Itoa(i.val)
}
func (l *ListExpr) string() (string, bool) {
elems := make([]string, len(l.List))
columnCount := 0
forceMulti := false
for i := 0; i < len(l.List); i++ {
if l.List[i].Type() == NodeListExpr {
forceMulti = true
}
elems[i] = l.List[i].String()
columnCount += len(elems[i])
}
if columnCount+len(elems) > 50 || forceMulti {
forceMulti = true
return "(\n\t" + strings.Join(elems, "\n\t") + "\n)", forceMulti
}
return "(" + strings.Join(elems, " ") + ")", false
}
func (l *ListExpr) String() string {
str, _ := l.string()
return str
}
func (c *ConcatExpr) String() string {
ret := ""
for i := 0; i < len(c.concat); i++ {
ret += c.concat[i].String()
if i < (len(c.concat) - 1) {
ret += "+"
}
}
return ret
}
func (v *VarExpr) String() string {
if v.IsVariadic {
return v.Name + "..."
}
return v.Name
}
func (i *IndexExpr) String() string {
ret := fmt.Sprintf("%s[%s]", i.Var, i.Index)
if i.IsVariadic {
return ret + "..."
}
return ret
}
func (l *BlockNode) adjustGroupAssign(node assignable, nodes []Node) {
var (
eqSpace int = node.getEqSpace()
i int
)
lhs := getlhs(node)
eqSpace = len(lhs) + 1
for i = 0; i < len(nodes); i++ {
assign, ok := nodes[i].(assignable)
if !ok {
break
}
if len(getlhs(assign))+1 > eqSpace {
eqSpace = len(getlhs(assign)) + 1
}
}
for j := 0; j < i; j++ {
knode := nodes[j].(assignable)
knode.setEqSpace(eqSpace)
}
node.setEqSpace(eqSpace)
}
func (l *BlockNode) String() string {
nodes := l.Nodes
content := make([]string, 0, 8192)
last := (len(nodes) - 1)
for i := 0; i < len(nodes); i++ {
addEOL := false
node := nodes[i]
nodebytes := node.String()
if i == 0 && node.Type() == NodeComment &&
strings.HasPrefix(node.String(), "#!") {
addEOL = true
} else if (node.Type() == NodeComment) && i < last {
nextNode := nodes[i+1]
if nextNode.Line() > node.Line()+1 {
addEOL = true
}
} else if i < last {
nextNode := nodes[i+1]
if node.Type() != nextNode.Type() {
addEOL = true
} else if node.Type() == NodeFnDecl {
addEOL = true
} else if node.Type() == NodeAssign || node.Type() == NodeExecAssign {
nodeAssign := node.(assignable)
if nodeAssign.getEqSpace() == -1 {
// lookahead to decide about best '=' distance
l.adjustGroupAssign(nodeAssign, nodes[i+1:])
}
nodebytes, addEOL = nodeAssign.string()
}
}
if addEOL {
nodebytes += "\n"
}
content = append(content, nodebytes)
}
return strings.Join(content, "\n")
}
// String returns the string representation of the import
func (n *ImportNode) String() string {
return `import ` + n.Path.String()
}
// String returns the string representation of assignment
func (n *SetenvNode) String() string {
if n.assign == nil {
return "setenv " + n.Name
}
return "setenv " + n.assign.String()
}
func (n *NameNode) String() string {
if n.Index != nil {
return n.Ident + "[" + n.Index.String() + "]"
}
return n.Ident
}
func (n *AssignNode) string() (string, bool) {
var (
multi bool
)
objs := n.Values
lhs := getlhs(n)
ret := ""
for i := 0; i < len(objs); i++ {
var (
objStr string
objmulti bool
)
obj := objs[i]
if obj.Type().IsExpr() {
if obj.Type() == NodeListExpr {
lobj := obj.(*ListExpr)
objStr, objmulti = lobj.string()
} else {
objStr = obj.String()
}
}
if i == 0 {
if n.eqSpace > len(lhs) && !multi {
ret = lhs + strings.Repeat(" ", n.eqSpace-len(lhs)) + "= " + objStr
} else {
ret = lhs + " = " + objStr
}
} else if i < len(objs)-1 {
ret = ret + ", " + objStr + ", "
} else {
ret = ret + ", " + objStr
}
if objmulti && !multi {
multi = true
}
}
return ret, multi
}
// String returns the string representation of assignment statement
func (n *AssignNode) String() string {
str, _ := n.string()
return str
}
func (n *ExecAssignNode) string() (string, bool) {
var (
cmdStr string
multi bool
)
lhs := getlhs(n)
if n.cmd.Type() == NodeCommand {
cmd := n.cmd.(*CommandNode)
cmdStr, multi = cmd.string()
} else if n.cmd.Type() == NodePipe {
cmd := n.cmd.(*PipeNode)
cmdStr, multi = cmd.string()
} else {
cmd := n.cmd.(*FnInvNode)
cmdStr, multi = cmd.string()
}
if n.eqSpace > len(lhs) {
ret := lhs + strings.Repeat(" ", n.eqSpace-len(lhs)) + "<= " + cmdStr
return ret, multi
}
return lhs + " <= " + cmdStr, multi
}
// String returns the string representation of command assignment statement
func (n *ExecAssignNode) String() string {
str, _ := n.string()
return str
}
func (n *CommandNode) toStringParts() ([]string, int) {
var (
content []string
line string
last = len(n.args) - 1
totalLen = 0
)
for i := 0; i < len(n.args); i += 2 {
var next string
arg := n.args[i].String()
if i < last {
next = n.args[i+1].String()
}
if i == 0 {
arg = n.name + " " + arg
}
if arg[0] == '-' {
if line != "" {
content = append(content, line)
line = ""
}
if len(next) > 0 && next[0] != '-' {
if line == "" {
line += arg + " " + next
} else {
line += " " + arg + " " + next
}
} else {
content = append(content, arg, next)
}
} else if next != "" {
if line == "" {
line += arg + " " + next
} else {
line += " " + arg + " " + next
}
} else {
if line == "" {
line += arg
} else {
line += " " + arg
}
}
totalLen += len(arg) + len(next) + 1
}
if line != "" {
content = append(content, line)
}
if len(content) == 0 {
content = append(content, n.name)
}
for i := 0; i < len(n.redirs); i++ {
rstr := n.redirs[i].String()
totalLen += len(rstr) + 1
content = append(content, rstr)
}
return content, totalLen
}
func (n *CommandNode) multiString() string {
content, totalLen := n.toStringParts()
if totalLen < 50 {
return "(" + strings.Join(content, " ") + ")"
}
content[0] = "\t" + content[0]
gentab := func(n int) string { return strings.Repeat("\t", n) }
tabLen := (len(content[0]) + 7) / 8
for i := 1; i < len(content); i++ {
content[i] = gentab(tabLen) + content[i]
}
return "(\n" + strings.Join(content, "\n") + "\n)"
}
// String returns the string representation of command statement
func (n *CommandNode) string() (string, bool) {
if n.multi {
return n.multiString(), true
}
var content []string
content = append(content, n.name)
for i := 0; i < len(n.args); i++ {
content = append(content, n.args[i].String())
}
for i := 0; i < len(n.redirs); i++ {
content = append(content, n.redirs[i].String())
}
return strings.Join(content, " "), false
}
func (n *CommandNode) String() string {
str, _ := n.string()
return str
}
func (n *PipeNode) multiString() string {
totalLen := 0
type cmdData struct {
content []string
totalLen int
}
content := make([]cmdData, len(n.cmds))
for i := 0; i < len(n.cmds); i++ {
cmdContent, cmdLen := n.cmds[i].toStringParts()
content[i] = cmdData{
cmdContent,
cmdLen,
}
totalLen += cmdLen
}
if totalLen+3 < 50 {
result := "("
for i := 0; i < len(content); i++ {
result += strings.Join(content[i].content, " ")
if i < len(content)-1 {
result += " | "
}
}
return result + ")"
}
gentab := func(n int) string { return strings.Repeat("\t", n) }
result := "(\n"
for i := 0; i < len(content); i++ {
cmdContent := content[i].content
cmdContent[0] = "\t" + cmdContent[0]
tabLen := (len(cmdContent[0]) + 7) / 8
for j := 1; j < len(cmdContent); j++ {
cmdContent[j] = gentab(tabLen) + cmdContent[j]
}
result += strings.Join(cmdContent, "\n")
if i < len(content)-1 {
result += " |\n"
}
}
return result + "\n)"
}
// String returns the string representation of pipeline statement
func (n *PipeNode) string() (string, bool) {
if n.multi {
return n.multiString(), true
}
ret := ""
for i := 0; i < len(n.cmds); i++ {
ret += n.cmds[i].String()
if i < (len(n.cmds) - 1) {
ret += " | "
}
}
return ret, false
}
func (n *PipeNode) String() string {
str, _ := n.string()
return str
}
// String returns the string representation of redirect
func (r *RedirectNode) String() string {
var result string
if r.rmap.lfd == r.rmap.rfd {
if r.location != nil {
return "> " + r.location.String()
}
return ""
}
if r.rmap.rfd >= 0 {
result = ">[" + strconv.Itoa(r.rmap.lfd) + "=" + strconv.Itoa(r.rmap.rfd) + "]"
} else if r.rmap.rfd == RedirMapNoValue {
result = ">[" + strconv.Itoa(r.rmap.lfd) + "]"
} else if r.rmap.rfd == RedirMapSupress {
result = ">[" + strconv.Itoa(r.rmap.lfd) + "=]"
}
if r.location != nil {
result = result + " " + r.location.String()
}
return result
}
// String returns the string representation of rfork statement
func (n *RforkNode) String() string {
rforkstr := "rfork " + n.arg.String()
tree := n.Tree()
if tree != nil {
rforkstr += " {\n"
block := tree.String()
stmts := strings.Split(block, "\n")
for i := 0; i < len(stmts); i++ {
stmts[i] = "\t" + stmts[i]
}
rforkstr += strings.Join(stmts, "\n") + "\n}"
}
return rforkstr
}
// String returns the string representation of comment
func (n *CommentNode) String() string {
return n.val
}
// String returns the string representation of if statement
func (n *IfNode) String() string {
var lstr, rstr string
lstr = n.lvalue.String()
rstr = n.rvalue.String()
ifStr := "if " + lstr + " " + n.op + " " + rstr + " {\n"
ifTree := n.IfTree()
block := ifTree.String()
stmts := strings.Split(block, "\n")
if strings.TrimSpace(block) != "" {
for i := 0; i < len(stmts); i++ {
stmts[i] = "\t" + stmts[i]
}
}
ifStr += strings.Join(stmts, "\n") + "\n}"
elseTree := n.ElseTree()
if elseTree != nil {
ifStr += " else "
elseBlock := elseTree.String()
elsestmts := strings.Split(elseBlock, "\n")
for i := 0; i < len(elsestmts); i++ {
if !n.IsElseIf() {
elsestmts[i] = "\t" + elsestmts[i]
}
}
if !n.IsElseIf() {
ifStr += "{\n"
}
ifStr += strings.Join(elsestmts, "\n")
if !n.IsElseIf() {
ifStr += "\n}"
}
}
return ifStr
}
func (n *VarAssignDeclNode) String() string { return "var " + n.Assign.String() }
func (n *VarExecAssignDeclNode) String() string { return "var " + n.ExecAssign.String() }
// String returns the string representation of function declaration
func (n *FnDeclNode) String() string {
fnStr := "fn"
if n.name != "" {
fnStr += " " + n.name + "("
}
for i := 0; i < len(n.args); i++ {
fnStr += n.args[i].String()
if i < (len(n.args) - 1) {
fnStr += ", "
}
}
fnStr += ") {\n"
tree := n.Tree()
stmts := strings.Split(tree.String(), "\n")
for i := 0; i < len(stmts); i++ {
if len(stmts[i]) > 0 {
fnStr += "\t" + stmts[i] + "\n"
} else {
fnStr += "\n"
}
}
fnStr += "}"
return fnStr
}
func (arg *FnArgNode) String() string {
ret := arg.Name
if arg.IsVariadic {
ret += "..."
}
return ret
}
// String returns the string representation of function invocation
func (n *FnInvNode) string() (string, bool) {
fnInvStr := n.name + "("
for i := 0; i < len(n.args); i++ {
fnInvStr += n.args[i].String()
if i < (len(n.args) - 1) {
fnInvStr += ", "
}
}
fnInvStr += ")"
return fnInvStr, false
}
func (n *FnInvNode) String() string {
str, _ := n.string()
return str
}
// String returns the string representation of bindfn
func (n *BindFnNode) String() string {
return "bindfn " + n.name + " " + n.cmdname
}
// String returns the string representation of return statement
func (n *ReturnNode) String() string {
var returns []string
ret := "return"
returnExprs := n.Returns
for i := 0; i < len(returnExprs); i++ {
returns = append(returns, returnExprs[i].String())
}
if len(returns) > 0 {
return ret + " " + strings.Join(returns, ", ")
}
return ret
}
// String returns the string representation of for statement
func (n *ForNode) String() string {
ret := "for"
if n.identifier != "" {
ret += " " + n.identifier + " in " + n.inExpr.String()
}
ret += " {\n"
tree := n.Tree()
stmts := strings.Split(tree.String(), "\n")
for i := 0; i < len(stmts); i++ {
if len(stmts[i]) > 0 {
ret += "\t" + stmts[i] + "\n"
} else {
ret += "\n"
}
}
ret += "}"
return ret
}
func stringify(s string) string {
buf := make([]byte, 0, len(s))
for i := 0; i < len(s); i++ {
switch s[i] {
case '"':
buf = append(buf, '\\', '"')
case '\t':
buf = append(buf, '\\', 't')
case '\n':
buf = append(buf, '\\', 'n')
case '\r':
buf = append(buf, '\\', 'r')
case '\\':
buf = append(buf, '\\', '\\')
default:
buf = append(buf, s[i])
}
}
return string(buf)
}
func getlhs(node assignable) string {
var nameStrs []string
nodeNames := node.names()
for i := 0; i < len(nodeNames); i++ {
nameStrs = append(nameStrs, nodeNames[i].String())
}
return strings.Join(nameStrs, ", ")
}
================================================
FILE: ast/node_fmt_test.go
================================================
package ast
import (
"testing"
"github.com/madlambda/nash/token"
)
func testPrinter(t *testing.T, node Node, expected string) {
if node.String() != expected {
t.Errorf("Values differ: '%s' != '%s'", node, expected)
}
}
func TestAstPrinterStringExpr(t *testing.T) {
for _, testcase := range []struct {
expected string
node Node
}{
// quote
{
expected: `"\""`,
node: NewStringExpr(token.NewFileInfo(1, 0), "\"", true),
},
// escape
{
expected: `"\\this is a test\n"`,
node: NewStringExpr(token.NewFileInfo(1, 0), "\\this is a test\n", true),
},
// tab
{
expected: `"this is a test\t"`,
node: NewStringExpr(token.NewFileInfo(1, 0), "this is a test\t", true),
},
// linefeed
{
expected: `"this is a test\n"`,
node: NewStringExpr(token.NewFileInfo(1, 0), "this is a test\n", true),
},
{
expected: `"\nthis is a test"`,
node: NewStringExpr(token.NewFileInfo(1, 0), "\nthis is a test", true),
},
{
expected: `"\n\n\n"`,
node: NewStringExpr(token.NewFileInfo(1, 0), "\n\n\n", true),
},
// carriege return
{
expected: `"this is a test\r"`,
node: NewStringExpr(token.NewFileInfo(1, 0), "this is a test\r", true),
},
{
expected: `"\rthis is a test"`,
node: NewStringExpr(token.NewFileInfo(1, 0), "\rthis is a test", true),
},
{
expected: `"\r\r\r"`,
node: NewStringExpr(token.NewFileInfo(1, 0), "\r\r\r", true),
},
} {
testPrinter(t, testcase.node, testcase.expected)
}
}
func TestASTPrinterAssignment(t *testing.T) {
zeroFileInfo := token.NewFileInfo(1, 0)
for _, testcase := range []struct {
expected string
node Node
}{
{
expected: `a = "1"`,
node: NewAssignNode(zeroFileInfo, []*NameNode{
NewNameNode(zeroFileInfo, "a", nil),
}, []Expr{NewStringExpr(zeroFileInfo, "1", true)}),
},
{
expected: `a = ()`,
node: NewAssignNode(zeroFileInfo, []*NameNode{
NewNameNode(zeroFileInfo, "a", nil),
}, []Expr{NewListExpr(zeroFileInfo, []Expr{})}),
},
{
expected: `a, b = (), ()`,
node: NewAssignNode(zeroFileInfo, []*NameNode{
NewNameNode(zeroFileInfo, "a", nil),
NewNameNode(zeroFileInfo, "b", nil),
}, []Expr{NewListExpr(zeroFileInfo, []Expr{}),
NewListExpr(zeroFileInfo, []Expr{})}),
},
{
expected: `a, b = "1", "2"`,
node: NewAssignNode(zeroFileInfo, []*NameNode{
NewNameNode(zeroFileInfo, "a", nil),
NewNameNode(zeroFileInfo, "b", nil),
}, []Expr{NewStringExpr(zeroFileInfo, "1", true),
NewStringExpr(zeroFileInfo, "2", true)}),
},
} {
testPrinter(t, testcase.node, testcase.expected)
}
}
================================================
FILE: ast/nodetype_string.go
================================================
// Code generated by "stringer -type=NodeType"; DO NOT EDIT
package ast
import "fmt"
const _NodeType_name = "NodeSetenvNodeBlockNodeNameNodeAssignNodeExecAssignNodeImportexecBeginNodeCommandNodePipeNodeRedirectNodeFnInvexecEndexpressionBeginNodeStringExprNodeIntExprNodeVarExprNodeListExprNodeIndexExprNodeConcatExprexpressionEndNodeStringNodeRforkNodeRforkFlagsNodeIfNodeCommentNodeFnArgNodeVarAssignDeclNodeVarExecAssignDeclNodeFnDeclNodeReturnNodeBindFnNodeDumpNodeFor"
var _NodeType_index = [...]uint16{0, 10, 19, 27, 37, 51, 61, 70, 81, 89, 101, 110, 117, 132, 146, 157, 168, 180, 193, 207, 220, 230, 239, 253, 259, 270, 279, 296, 317, 327, 337, 347, 355, 362}
func (i NodeType) String() string {
i -= 1
if i < 0 || i >= NodeType(len(_NodeType_index)-1) {
return fmt.Sprintf("NodeType(%d)", i+1)
}
return _NodeType_name[_NodeType_index[i]:_NodeType_index[i+1]]
}
================================================
FILE: ast/tree.go
================================================
package ast
type (
// Tree is the AST
Tree struct {
Name string
Root *BlockNode // top-level root of the tree.
}
)
// NewTree creates a new AST tree
func NewTree(name string) *Tree {
return &Tree{
Name: name,
}
}
func (t *Tree) IsEqual(other *Tree) bool {
if t == other {
return true
}
return t.Root.IsEqual(other.Root)
}
func (tree *Tree) String() string {
if tree.Root == nil {
return ""
}
if len(tree.Root.Nodes) == 0 {
return ""
}
return tree.Root.String()
}
================================================
FILE: ast/tree_test.go
================================================
package ast
import (
"testing"
"github.com/madlambda/nash/token"
)
// Test API
func TestTreeCreation(t *testing.T) {
tr := NewTree("example")
if tr.Name != "example" {
t.Errorf("Invalid name")
return
}
}
func TestTreeRawCreation(t *testing.T) {
tr := NewTree("creating a tree by hand")
ln := NewBlockNode(token.NewFileInfo(1, 0))
rfarg := NewStringExpr(token.NewFileInfo(1, 0), "unp", false)
r := NewRforkNode(token.NewFileInfo(1, 0))
r.SetFlags(rfarg)
ln.Push(r)
tr.Root = ln
if tr.String() != "rfork unp" {
t.Error("Failed to build AST by hand")
}
}
================================================
FILE: cmd/nash/cli.go
================================================
package main
import (
"bytes"
"fmt"
"io"
"os"
"strings"
"github.com/madlambda/nash"
"github.com/madlambda/nash/ast"
"github.com/madlambda/nash/parser"
"github.com/madlambda/nash/sh"
"github.com/chzyer/readline"
)
type (
Interrupted interface {
Interrupted() bool
}
Ignored interface {
Ignore() bool
}
BlockNotFinished interface {
Unfinished() bool
}
)
var completers = []readline.PrefixCompleterInterface{}
func execFn(shell *nash.Shell, fnDef sh.FnDef, args []sh.Obj) {
fn := fnDef.Build()
err := fn.SetArgs(args)
if err != nil {
fmt.Fprintf(os.Stderr, "%s failed: %s\n", fnDef.Name(), err.Error())
}
fn.SetStdin(shell.Stdin())
fn.SetStdout(shell.Stdout())
fn.SetStderr(shell.Stderr())
if err := fn.Start(); err != nil {
fmt.Fprintf(os.Stderr, "%s failed: %s\n", fnDef.Name(), err.Error())
return
}
if err := fn.Wait(); err != nil {
fmt.Fprintf(os.Stderr, "%s failed: %s\n", fnDef.Name(), err.Error())
return
}
}
func importInitFile(shell *nash.Shell, initFile string) (bool, error) {
if d, err := os.Stat(initFile); err == nil {
if m := d.Mode(); !m.IsDir() {
err := shell.ExecuteString("init",
fmt.Sprintf("import %q", initFile))
if err != nil {
return false, fmt.Errorf("Failed to evaluate '%s': %s", initFile, err.Error())
}
return true, nil
}
}
return false, nil
}
func loadInit(shell *nash.Shell) error {
if noInit {
return nil
}
initFiles := []string{
shell.NashPath() + "/init",
shell.NashPath() + "/init.sh",
}
for _, init := range initFiles {
imported, err := importInitFile(shell, init)
if err != nil {
return err
}
if imported {
break
}
}
return nil
}
func cli(shell *nash.Shell) error {
shell.SetInteractive(true)
if err := loadInit(shell); err != nil {
fmt.Fprintf(os.Stderr, "error loading init file:\n%s\n", err)
}
historyFile := shell.NashPath() + "/history"
cfg := readline.Config{
Prompt: shell.Prompt(),
HistoryFile: historyFile,
InterruptPrompt: "^C",
EOFPrompt: "exit",
}
term, err := readline.NewTerminal(&cfg)
if err != nil {
return err
}
op := term.Readline()
rline := &readline.Instance{
Config: &cfg,
Terminal: term,
Operation: op,
}
defer rline.Close()
completer := NewCompleter(op, term, shell)
cfg.AutoComplete = completer
if lineMode, ok := shell.Getvar("LINEMODE"); ok {
if lineStr, ok := lineMode.(*sh.StrObj); ok && lineStr.Str() == "vim" {
rline.SetVimMode(true)
} else {
rline.SetVimMode(false)
}
}
return docli(shell, rline)
}
func docli(shell *nash.Shell, rline *readline.Instance) error {
var (
content bytes.Buffer
lineidx int
line string
parse *parser.Parser
tr *ast.Tree
err error
unfinished bool
prompt string
)
for {
if fnDef, err := shell.GetFn("nash_repl_before"); err == nil && !unfinished {
execFn(shell, fnDef, nil)
}
if !unfinished {
prompt = shell.Prompt()
}
rline.SetPrompt(prompt)
line, err = rline.Readline()
if err == readline.ErrInterrupt {
goto cont
} else if err == io.EOF {
err = nil
break
}
lineidx++
line = strings.TrimSpace(line)
// handle special cli commands
switch {
case strings.HasPrefix(line, "set mode "):
switch line[9:] {
case "vi":
rline.SetVimMode(true)
case "emacs":
rline.SetVimMode(false)
default:
fmt.Printf("invalid mode: %s\n", line[9:])
}
goto cont
case line == "mode":
if rline.IsVimMode() {
fmt.Printf("Current mode: vim\n")
} else {
fmt.Printf("Current mode: emacs\n")
}
goto cont
case line == "exit":
break
}
content.Write([]byte(line + "\n"))
parse = parser.NewParser(fmt.Sprintf("<stdin line %d>", lineidx), string(content.Bytes()))
line = string(content.Bytes())
tr, err = parse.Parse()
if err != nil {
if interrupted, ok := err.(Interrupted); ok && interrupted.Interrupted() {
content.Reset()
goto cont
} else if errBlock, ok := err.(BlockNotFinished); ok && errBlock.Unfinished() {
prompt = ">>> "
unfinished = true
goto cont
}
fmt.Printf("ERROR: %s\n", err.Error())
content.Reset()
goto cont
}
unfinished = false
content.Reset()
_, err = shell.ExecuteTree(tr)
if err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
cont:
if fnDef, err := shell.GetFn("nash_repl_after"); err == nil && !unfinished {
var status sh.Obj
var ok bool
if status, ok = shell.Getvar("status"); !ok {
status = sh.NewStrObj("")
}
execFn(shell, fnDef, []sh.Obj{sh.NewStrObj(line), status})
}
rline.SetPrompt(prompt)
}
return nil
}
================================================
FILE: cmd/nash/completer.go
================================================
package main
import (
"fmt"
"os"
"strconv"
"github.com/madlambda/nash"
"github.com/madlambda/nash/sh"
"github.com/chzyer/readline"
)
var runes = readline.Runes{}
type Completer struct {
op *readline.Operation
term *readline.Terminal
sh *nash.Shell
}
func NewCompleter(op *readline.Operation, term *readline.Terminal, sh *nash.Shell) *Completer {
return &Completer{op, term, sh}
}
func (c *Completer) Do(line []rune, pos int) ([][]rune, int) {
var (
newLine [][]rune
offset int
lineArg = sh.NewStrObj(string(line))
posArg = sh.NewStrObj(strconv.Itoa(pos))
)
defer c.op.Refresh()
defer c.term.PauseRead(false)
fnDef, err := c.sh.GetFn("nash_complete")
if err != nil {
// no complete available
return [][]rune{[]rune{'\t'}}, offset
}
nashFunc := fnDef.Build()
err = nashFunc.SetArgs([]sh.Obj{lineArg, posArg})
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to autocomplete: %s\n", err.Error())
return newLine, offset
}
nashFunc.SetStdin(c.sh.Stdin())
nashFunc.SetStdout(c.sh.Stdout())
nashFunc.SetStderr(c.sh.Stderr())
if err = nashFunc.Start(); err != nil {
fmt.Fprintf(os.Stderr, "Failed to autocomplete: %s\n", err.Error())
return newLine, offset
}
if err = nashFunc.Wait(); err != nil {
fmt.Fprintf(os.Stderr, "Failed to autocomplete: %s\n", err.Error())
return newLine, offset
}
ret := nashFunc.Results()
if len(ret) != 1 || ret[0].Type() != sh.ListType {
fmt.Fprintf(os.Stderr, "ignoring autocomplete value: %v\n", ret)
return newLine, offset
}
retval := ret[0]
retlist := retval.(*sh.ListObj)
if len(retlist.List()) != 2 {
return newLine, pos
}
newline := retlist.List()[0]
newpos := retlist.List()[1]
if newline.Type() != sh.StringType || newpos.Type() != sh.StringType {
fmt.Fprintf(os.Stderr, "ignoring autocomplete value: (%s) (%s)\n", newline, newpos)
return newLine, offset
}
objline := newline.(*sh.StrObj)
objpos := newpos.(*sh.StrObj)
newoffset, err := strconv.Atoi(objpos.Str())
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to autocomplete: %s\n", err.Error())
return newLine, offset
}
newLine = append(newLine, []rune(objline.Str()))
return newLine, newoffset
}
================================================
FILE: cmd/nash/env.go
================================================
package main
import (
"errors"
"os"
"path/filepath"
)
func NashPath() (string, error) {
nashpath := os.Getenv("NASHPATH")
if nashpath != "" {
return nashpath, nil
}
h, err := home()
return filepath.Join(h, "nash"), err
}
func NashRoot() (string, error) {
nashroot, ok := os.LookupEnv("NASHROOT")
if ok {
return nashroot, nil
}
h, err := home()
return filepath.Join(h, "nashroot"), err
}
func home() (string, error) {
homedir, err := os.UserHomeDir()
if err != nil {
return "", err
}
if homedir == "" {
return "", errors.New("invalid empty home dir")
}
return homedir, nil
}
================================================
FILE: cmd/nash/env_test.go
================================================
package main_test
import (
"os"
"path/filepath"
"strings"
"testing"
main "github.com/madlambda/nash/cmd/nash"
)
// TODO: No idea on how to inject failures like empty HOME folders for now
func TestLoadNASHPATH(t *testing.T) {
defaultNashPath := filepath.Join(home(t), "nash")
runTests(t, main.NashPath, []EnvTest{
{
name: "Exported",
env: map[string]string{
"NASHPATH": filepath.Join("etc", "nash"),
},
want: filepath.Join("etc", "nash"),
},
{
name: "IgnoresNASHROOT",
env: map[string]string{
"NASHROOT": "/etc/nashroot/tests",
"HOME": home(t),
},
want: defaultNashPath,
},
{
name: "UseUserHomeWhenUnset",
env: map[string]string{
"NASHROOT": "/etc/nashroot/tests",
"HOME": home(t),
},
want: defaultNashPath,
},
})
}
func TestLoadNASHROOT(t *testing.T) {
defaultNashRoot := filepath.Join(home(t), "nashroot")
runTests(t, main.NashRoot, []EnvTest{
{
name: "Exported",
env: map[string]string{
"NASHROOT": filepath.Join("etc", "nashroot"),
},
want: filepath.Join("etc", "nashroot"),
},
{
name: "IgnoresGOPATHIfSet",
env: map[string]string{
"GOPATH": filepath.Join("go", "natel", "review"),
"NASHROOT": filepath.Join("nashroot", "ignoredgopath"),
},
want: filepath.Join("nashroot", "ignoredgopath"),
},
{
name: "UsesHOMEevenWhenGOPATHIsSet",
env: map[string]string{
"HOME": home(t),
"GOPATH": filepath.Join("go", "path"),
},
want: defaultNashRoot,
},
{
name: "UsesUserHomeWhenNASHROOTAndGOPATHAreUnset",
env: map[string]string{
"HOME": home(t),
},
want: filepath.Join(home(t), "nashroot"),
},
})
}
func runTests(t *testing.T, testfunc func() (string, error), cases []EnvTest) {
t.Helper()
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
restore := clearenv(t)
defer restore()
export(t, c.env)
got, err := testfunc()
if err != nil {
t.Fatal(err)
}
if got != c.want {
t.Fatalf("got[%s] != want[%s]", got, c.want)
}
})
}
}
type EnvTest struct {
name string
env map[string]string
want string
}
func clearenv(t *testing.T) func() {
env := os.Environ()
os.Clearenv()
return func() {
for _, envvar := range env {
parsed := strings.Split(envvar, "=")
name := parsed[0]
val := strings.Join(parsed[1:], "=")
err := os.Setenv(name, val)
if err != nil {
t.Fatalf("error[%s] restoring env var[%s]", err, envvar)
}
}
}
}
func export(t *testing.T, env map[string]string) {
t.Helper()
for name, val := range env {
err := os.Setenv(name, val)
if err != nil {
t.Fatal(err)
}
}
}
func home(t *testing.T) string {
t.Helper()
homedir, err := os.UserHomeDir()
if err != nil {
t.Fatal(err)
}
return homedir
}
================================================
FILE: cmd/nash/example.sh
================================================
#!/usr/bin/env nash
-rm -rf rootfs
rfork upmis {
mount -t proc proc /proc
mkdir -p rootfs
mount -t tmpfs -o size=1G tmpfs rootfs
cd rootfs
wget "https://busybox.net/downloads/binaries/latest/busybox-x86_64" -O busybox
chmod +x busybox
mkdir bin
./busybox --install ./bin
mkdir -p proc
mkdir -p dev
mount -t proc proc proc
mount -t tmpfs tmpfs dev
cp ../nash .
chroot . /bin/sh
}
================================================
FILE: cmd/nash/install.go
================================================
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
)
func NashLibDir(nashpath string) string {
//FIXME: This is sadly duplicated from the shell implementation =(
return filepath.Join(nashpath, "lib")
}
func InstallLib(nashpath string, sourcepath string) error {
nashlibdir := NashLibDir(nashpath)
sourcepathAbs, err := filepath.Abs(sourcepath)
if err != nil {
return fmt.Errorf("error[%s] getting absolute path of [%s]", err, sourcepath)
}
if filepath.HasPrefix(sourcepathAbs, nashlibdir) {
return fmt.Errorf(
"lib source path[%s] can't be inside nash lib dir[%s]", sourcepath, nashlibdir)
}
return installLib(nashlibdir, sourcepathAbs)
}
func installLib(targetdir string, sourcepath string) error {
f, err := os.Stat(sourcepath)
if err != nil {
return fmt.Errorf("error[%s] checking if path[%s] is dir", err, sourcepath)
}
if !f.IsDir() {
return copyfile(targetdir, sourcepath)
}
basedir := filepath.Base(sourcepath)
targetdir = filepath.Join(targetdir, basedir)
files, err := ioutil.ReadDir(sourcepath)
if err != nil {
return fmt.Errorf("error[%s] reading dir[%s]", err, sourcepath)
}
for _, file := range files {
err := installLib(targetdir, filepath.Join(sourcepath, file.Name()))
if err != nil {
return err
}
}
return nil
}
func copyfile(targetdir string, sourcefilepath string) error {
fail := func(err error) error {
return fmt.Errorf(
"error[%s] trying to copy file[%s] to [%s]", err, sourcefilepath, targetdir)
}
err := os.MkdirAll(targetdir, os.ModePerm)
if err != nil {
return fail(err)
}
sourcefile, err := os.Open(sourcefilepath)
if err != nil {
return fail(err)
}
defer sourcefile.Close()
targetfilepath := filepath.Join(targetdir, filepath.Base(sourcefilepath))
targetfile, err := os.Create(targetfilepath)
if err != nil {
return fail(err)
}
defer targetfile.Close()
_, err = io.Copy(targetfile, sourcefile)
return err
}
================================================
FILE: cmd/nash/install_test.go
================================================
package main_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
main "github.com/madlambda/nash/cmd/nash"
"github.com/madlambda/nash/internal/testing/fixture"
)
// TODO: test when nashpath lib already exists and has libraries inside
func TestInstallLib(t *testing.T) {
type testcase struct {
name string
libfiles []string
installpath string
// want will map the wanted files to the original files copied from the lib
// the wanted files paths are relative to inside the nashpath lib dir.
// the files need to be mapped to the original files because of content validation
// when multiple files are installed.
want map[string]string
}
cases := []testcase{
{
name: "SingleFile",
libfiles: []string{
"/testfile/file.sh",
},
installpath: "/testfile/file.sh",
want: map[string]string{
"file.sh": "/testfile/file.sh",
},
},
{
name: "SingleDir",
libfiles: []string{
"/testfile/file.sh",
},
installpath: "/testfile",
want: map[string]string{
"/testfile/file.sh": "/testfile/file.sh",
},
},
{
name: "SingleDirWithMultipleFiles",
libfiles: []string{
"/testfile/file.sh",
"/testfile/fileagain.sh",
},
installpath: "/testfile",
want: map[string]string{
"/testfile/file.sh": "/testfile/file.sh",
"/testfile/fileagain.sh": "/testfile/fileagain.sh",
},
},
{
name: "MultipleDirsWithMultipleFiles",
libfiles: []string{
"/testfile/file.sh",
"/testfile/dir1/file.sh",
"/testfile/dir1/fileagain.sh",
"/testfile/dir2/file.sh",
"/testfile/dir2/fileagain.sh",
"/testfile/dir2/dir3/file.sh",
},
installpath: "/testfile",
want: map[string]string{
"/testfile/file.sh": "/testfile/file.sh",
"/testfile/dir1/file.sh": "/testfile/dir1/file.sh",
"/testfile/dir1/fileagain.sh": "/testfile/dir1/fileagain.sh",
"/testfile/dir2/file.sh": "/testfile/dir2/file.sh",
"/testfile/dir2/fileagain.sh": "/testfile/dir2/fileagain.sh",
"/testfile/dir2/dir3/file.sh": "/testfile/dir2/dir3/file.sh",
},
},
{
name: "InstallOnlyFilesIndicatedByInstallDir",
libfiles: []string{
"/testfile/file.sh",
"/testfile/dir1/file.sh",
"/testfile/dir1/fileagain.sh",
"/testfile/dir2/file.sh",
"/testfile/dir2/fileagain.sh",
"/testfile/dir2/dir3/file.sh",
},
installpath: "/testfile/dir2",
want: map[string]string{
"/dir2/file.sh": "/testfile/dir2/file.sh",
"/dir2/fileagain.sh": "/testfile/dir2/fileagain.sh",
"/dir2/dir3/file.sh": "/testfile/dir2/dir3/file.sh",
},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
nashpath, rmnashpath := fixture.Tmpdir(t)
defer rmnashpath()
libfilesDir, rmlibfilesDir := fixture.Tmpdir(t)
defer rmlibfilesDir()
nashlibdir := main.NashLibDir(nashpath)
libfiles := []string{}
libfileFullPath := func(libfilepath string) string {
return filepath.Join(libfilesDir, libfilepath)
}
for _, f := range c.libfiles {
libfiles = append(libfiles, libfileFullPath(f))
}
createdLibFiles := fixture.CreateFiles(t, libfiles)
installpath := filepath.Join(libfilesDir, c.installpath)
err := main.InstallLib(nashpath, installpath)
if err != nil {
t.Fatal(err)
}
listNashPathFiles := func() []string {
files := []string{}
filepath.Walk(nashpath, func(path string, stats os.FileInfo, err error) error {
if stats.IsDir() {
return nil
}
files = append(files, path)
return nil
})
return files
}
gotFiles := listNashPathFiles()
fatal := func() {
t.Errorf("nashpath: [%s]", nashpath)
t.Errorf("nashpath contents:")
for _, path := range gotFiles {
t.Errorf("[%s]", path)
}
t.Fatal("")
}
if len(gotFiles) != len(c.want) {
t.Errorf("wanted[%d] files but got[%d]", len(c.want), len(gotFiles))
fatal()
}
for wantFilepath, libfilepath := range c.want {
completeLibFilepath := libfileFullPath(libfilepath)
wantContents, ok := createdLibFiles[completeLibFilepath]
if !ok {
t.Errorf("unable to find libfilepath[%s] contents on created lib files map[%+v]", completeLibFilepath, createdLibFiles)
t.Fatal("this probably means a wrongly specified test case with wanted files that are not present on the libfiles")
}
fullWantFilepath := filepath.Join(nashlibdir, wantFilepath)
wantFile, err := os.Open(fullWantFilepath)
if err != nil {
t.Errorf("error[%s] checking wanted file[%s]", err, wantFilepath)
fatal()
}
gotContentsRaw, err := ioutil.ReadAll(wantFile)
wantFile.Close()
if err != nil {
t.Errorf("error[%s] checking existence of wanted file[%s]", err, wantFilepath)
fatal()
}
gotContents := string(gotContentsRaw)
if gotContents != wantContents {
t.Errorf("for file [%s] wanted contents [%s] but got [%s]", wantFilepath, wantContents, gotContents)
fatal()
}
}
})
}
}
func TestSourcePathCantBeEqualToNashLibDir(t *testing.T) {
nashpath, rmnashpath := fixture.Tmpdir(t)
defer rmnashpath()
nashlibdir := main.NashLibDir(nashpath)
fixture.CreateFile(t, filepath.Join(nashlibdir, "whatever.sh"))
assertInstallLibFails(t, nashpath, nashlibdir)
}
func TestSourcePathCantBeInsideNashLibDir(t *testing.T) {
nashpath, rmnashpath := fixture.Tmpdir(t)
defer rmnashpath()
nashlibdir := main.NashLibDir(nashpath)
sourcelibdir := filepath.Join(nashlibdir, "somedir")
fixture.CreateFile(t, filepath.Join(sourcelibdir, "whatever.sh"))
assertInstallLibFails(t, nashpath, sourcelibdir)
}
func TestRelativeSourcePathCantBeInsideNashLibDir(t *testing.T) {
nashpath, rmnashpath := fixture.Tmpdir(t)
defer rmnashpath()
nashlibdir := main.NashLibDir(nashpath)
fixture.CreateFile(t, filepath.Join(nashlibdir, "somedir", "whatever.sh"))
oldwd := fixture.WorkingDir(t)
defer fixture.ChangeDir(t, oldwd)
fixture.ChangeDir(t, nashlibdir)
assertInstallLibFails(t, nashpath, "./somedir")
}
func TestFailsOnUnexistentSourcePath(t *testing.T) {
nashpath, rmnashpath := fixture.Tmpdir(t)
defer rmnashpath()
assertInstallLibFails(t, nashpath, "/nonexistent/nash/crap")
}
func TestFailsOnUnreadableSourcePath(t *testing.T) {
nashpath, rmnashpath := fixture.Tmpdir(t)
defer rmnashpath()
sourcedir, rmsourcedir := fixture.Tmpdir(t)
defer rmsourcedir()
fixture.Chmod(t, sourcedir, writeOnly)
assertInstallLibFails(t, nashpath, sourcedir)
}
func TestFailsOnUnreadableFileInsideSourcePath(t *testing.T) {
nashpath, rmnashpath := fixture.Tmpdir(t)
defer rmnashpath()
sourcedir, rmsourcedir := fixture.Tmpdir(t)
defer rmsourcedir()
readableFile := filepath.Join(sourcedir, "file1.sh")
unreadableFile := filepath.Join(sourcedir, "file2.sh")
fixture.CreateFiles(t, []string{readableFile, unreadableFile})
fixture.Chmod(t, unreadableFile, writeOnly)
assertInstallLibFails(t, nashpath, sourcedir)
}
func TestFailsOnUnwriteableNashPath(t *testing.T) {
nashpath, rmnashpath := fixture.Tmpdir(t)
defer rmnashpath()
sourcedir, rmsourcedir := fixture.Tmpdir(t)
defer rmsourcedir()
fixture.Chmod(t, nashpath, readOnly)
fixture.CreateFile(t, filepath.Join(sourcedir, "file.sh"))
assertInstallLibFails(t, nashpath, sourcedir)
}
func TestFailsOnUnwriteableFileInsideNashLibdir(t *testing.T) {
nashpath, rmnashpath := fixture.Tmpdir(t)
defer rmnashpath()
sourcedir, rmsourcedir := fixture.Tmpdir(t)
defer rmsourcedir()
filename := "test.sh"
sourcefile := filepath.Join(sourcedir, filename)
expectedInstalledFile := filepath.Join(
main.NashLibDir(nashpath),
filepath.Base(sourcedir),
filename,
)
fixture.CreateFiles(t, []string{sourcefile, expectedInstalledFile})
fixture.Chmod(t, expectedInstalledFile, readOnly)
assertInstallLibFails(t, nashpath, sourcedir)
}
func assertInstallLibFails(t *testing.T, nashpath string, sourcepath string) {
t.Helper()
err := main.InstallLib(nashpath, sourcepath)
if err == nil {
t.Fatal("expected error, got nil")
}
}
const writeOnly = 0333
const readOnly = 0555
================================================
FILE: cmd/nash/main.go
================================================
// Package main has two sides:
// - User mode: shell
// - tool mode: unix socket server for handling namespace operations
// When started, the program choses their side based on the argv[0].
// The name "nash" indicates a user shell and the name -nashd- indicates
// the namespace server tool.
package main
import (
"flag"
"fmt"
"os"
"github.com/madlambda/nash"
)
var (
// version is set at build time
VersionString = "No version provided"
version bool
debug bool
file string
command string
addr string
noInit bool
interactive bool
install string
)
func init() {
flag.BoolVar(&version, "version", false, "Show version")
flag.BoolVar(&debug, "debug", false, "enable debug")
flag.BoolVar(&noInit, "noinit", false, "do not load init/init.sh file")
flag.StringVar(&command, "c", "", "command to execute")
flag.StringVar(&install, "install", "", "path of the library that you want to install (can be a single file)")
flag.BoolVar(&interactive, "i", false, "Interactive mode (default if no args)")
if os.Args[0] == "-nashd-" || (len(os.Args) > 1 && os.Args[1] == "-daemon") {
flag.Bool("daemon", false, "force enable nashd mode")
flag.StringVar(&addr, "addr", "", "rcd unix file")
}
}
func main() {
var args []string
var shell *nash.Shell
var err error
flag.Parse()
if version {
fmt.Printf("build tag: %s\n", VersionString)
return
}
if install != "" {
fmt.Printf("installing library located at [%s]\n", install)
np, err := NashPath()
if err != nil {
fmt.Printf("error[%s] getting NASHPATH, cant install library\n", err)
os.Exit(1)
}
err = InstallLib(np, install)
if err != nil {
fmt.Printf("error[%s] installing library\n", err)
os.Exit(1)
}
fmt.Println("installed with success")
return
}
if len(flag.Args()) > 0 {
args = flag.Args()
file = args[0]
}
if shell, err = initShell(); err != nil {
goto Error
}
shell.SetDebug(debug)
if addr != "" {
startNashd(shell, addr)
return
}
if (file == "" && command == "") || interactive {
if err = cli(shell); err != nil {
goto Error
}
return
}
if file != "" {
if err = shell.ExecFile(file, args...); err != nil {
goto Error
}
}
if command != "" {
err = shell.ExecuteString("<argument -c>", command)
if err != nil {
goto Error
}
}
Error:
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
}
func initShell() (*nash.Shell, error) {
nashpath, err := NashPath()
if err != nil {
return nil, err
}
nashroot, err := NashRoot()
if err != nil {
return nil, err
}
os.Mkdir(nashpath, 0755)
return nash.New(nashpath, nashroot)
}
================================================
FILE: cmd/nash/rpc.go
================================================
package main
import (
"fmt"
"io"
"net"
"os"
"github.com/madlambda/nash"
)
func serveConn(sh *nash.Shell, conn net.Conn) {
var data [1024]byte
for {
n, err := conn.Read(data[:])
if err != nil {
if err == io.EOF {
return
}
fmt.Printf("Failed to read data: %s", err.Error())
return
}
if string(data[0:n]) == "quit" {
return
}
err = sh.ExecuteString("-nashd-", string(data[0:n]))
if err != nil {
fmt.Printf("nashd: %s\n", err.Error())
_, err = conn.Write([]byte("1"))
if err != nil {
fmt.Printf("Failed to send command status.\n")
return
}
} else {
_, err = conn.Write([]byte("0"))
if err != nil {
fmt.Printf("Failed to send command status.\n")
return
}
}
}
}
func startNashd(sh *nash.Shell, socketPath string) {
os.Remove(socketPath)
addr := &net.UnixAddr{
Net: "unix",
Name: socketPath,
}
listener, err := net.ListenUnix("unix", addr)
if err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
return
}
// Accept only one connection
conn, err := listener.AcceptUnix()
if err != nil {
fmt.Printf("ERROR: %v", err.Error())
}
serveConn(sh, conn)
listener.Close()
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/.travis.yml
================================================
language: go
go:
- 1.5
- 1.7
script:
- GOOS=windows go install github.com/chzyer/readline/example/...
- GOOS=linux go install github.com/chzyer/readline/example/...
- GOOS=darwin go install github.com/chzyer/readline/example/...
- go test -race -v
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/CHANGELOG.md
================================================
# ChangeLog
### 1.4 - 2016-07-25
* [#60][60] Support dynamic autocompletion
* Fix ANSI parser on Windows
* Fix wrong column width in complete mode on Windows
* Remove dependent package "golang.org/x/crypto/ssh/terminal"
### 1.3 - 2016-05-09
* [#38][38] add SetChildren for prefix completer interface
* [#42][42] improve multiple lines compatibility
* [#43][43] remove sub-package(runes) for gopkg compatiblity
* [#46][46] Auto complete with space prefixed line
* [#48][48] support suspend process (ctrl+Z)
* [#49][49] fix bug that check equals with previous command
* [#53][53] Fix bug which causes integer divide by zero panicking when input buffer is empty
### 1.2 - 2016-03-05
* Add a demo for checking password strength [example/readline-pass-strength](https://github.com/chzyer/readline/blob/master/example/readline-pass-strength/readline-pass-strength.go), , written by [@sahib](https://github.com/sahib)
* [#23][23], support stdin remapping
* [#27][27], add a `UniqueEditLine` to `Config`, which will erase the editing line after user submited it, usually use in IM.
* Add a demo for multiline [example/readline-multiline](https://github.com/chzyer/readline/blob/master/example/readline-multiline/readline-multiline.go) which can submit one SQL by multiple lines.
* Supports performs even stdin/stdout is not a tty.
* Add a new simple apis for single instance, check by [here](https://github.com/chzyer/readline/blob/master/std.go). It need to save history manually if using this api.
* [#28][28], fixes the history is not working as expected.
* [#33][33], vim mode now support `c`, `d`, `x (delete character)`, `r (replace character)`
### 1.1 - 2015-11-20
* [#12][12] Add support for key `<Delete>`/`<Home>`/`<End>`
* Only enter raw mode as needed (calling `Readline()`), program will receive signal(e.g. Ctrl+C) if not interact with `readline`.
* Bugs fixed for `PrefixCompleter`
* Press `Ctrl+D` in empty line will cause `io.EOF` in error, Press `Ctrl+C` in anytime will cause `ErrInterrupt` instead of `io.EOF`, this will privodes a shell-like user experience.
* Customable Interrupt/EOF prompt in `Config`
* [#17][17] Change atomic package to use 32bit function to let it runnable on arm 32bit devices
* Provides a new password user experience(`readline.ReadPasswordEx()`).
### 1.0 - 2015-10-14
* Initial public release.
[12]: https://github.com/chzyer/readline/pull/12
[17]: https://github.com/chzyer/readline/pull/17
[23]: https://github.com/chzyer/readline/pull/23
[27]: https://github.com/chzyer/readline/pull/27
[28]: https://github.com/chzyer/readline/pull/28
[33]: https://github.com/chzyer/readline/pull/33
[38]: https://github.com/chzyer/readline/pull/38
[42]: https://github.com/chzyer/readline/pull/42
[43]: https://github.com/chzyer/readline/pull/43
[46]: https://github.com/chzyer/readline/pull/46
[48]: https://github.com/chzyer/readline/pull/48
[49]: https://github.com/chzyer/readline/pull/49
[53]: https://github.com/chzyer/readline/pull/53
[60]: https://github.com/chzyer/readline/pull/60
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Chzyer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/README.md
================================================
[](https://travis-ci.org/chzyer/readline)
[](LICENSE.md)
[](https://github.com/chzyer/readline/releases)
[](https://godoc.org/github.com/chzyer/readline)
[](#backers)
[](#sponsors)
<p align="center">
<img src="https://raw.githubusercontent.com/chzyer/readline/assets/logo.png" />
<a href="https://asciinema.org/a/32oseof9mkilg7t7d4780qt4m" target="_blank"><img src="https://asciinema.org/a/32oseof9mkilg7t7d4780qt4m.png" width="654"/></a>
<img src="https://raw.githubusercontent.com/chzyer/readline/assets/logo_f.png" />
</p>
A powerful readline library in `Linux` `macOS` `Windows`
## Guide
* [Demo](example/readline-demo/readline-demo.go)
* [Shortcut](doc/shortcut.md)
## Repos using readline
[](https://github.com/cockroachdb/cockroach)
[](https://github.com/remind101/empire)
[](https://github.com/youtube/doorman)
[](https://github.com/bom-d-van/harp)
[](https://github.com/abiosoft/ishell)
[](https://github.com/robertkrimen/otto)
[](https://github.com/Netflix/hal-9001)
[](https://github.com/docker/go-p9p)
[](https://github.com/mehrdadrad/mylg)
## Feedback
If you have any questions, please submit a github issue and any pull requests is welcomed :)
* [https://twitter.com/chzyer](https://twitter.com/chzyer)
* [http://weibo.com/2145262190](http://weibo.com/2145262190)
## Backers
Love Readline? Help me keep it alive by donating funds to cover project expenses!<br />
[[Become a backer](https://opencollective.com/readline#backer)]
<a href="https://opencollective.com/readline/backer/0/website" target="_blank"><img src="https://opencollective.com/readline/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/1/website" target="_blank"><img src="https://opencollective.com/readline/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/2/website" target="_blank"><img src="https://opencollective.com/readline/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/3/website" target="_blank"><img src="https://opencollective.com/readline/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/4/website" target="_blank"><img src="https://opencollective.com/readline/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/5/website" target="_blank"><img src="https://opencollective.com/readline/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/6/website" target="_blank"><img src="https://opencollective.com/readline/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/7/website" target="_blank"><img src="https://opencollective.com/readline/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/8/website" target="_blank"><img src="https://opencollective.com/readline/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/9/website" target="_blank"><img src="https://opencollective.com/readline/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/10/website" target="_blank"><img src="https://opencollective.com/readline/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/11/website" target="_blank"><img src="https://opencollective.com/readline/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/12/website" target="_blank"><img src="https://opencollective.com/readline/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/13/website" target="_blank"><img src="https://opencollective.com/readline/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/14/website" target="_blank"><img src="https://opencollective.com/readline/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/15/website" target="_blank"><img src="https://opencollective.com/readline/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/16/website" target="_blank"><img src="https://opencollective.com/readline/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/17/website" target="_blank"><img src="https://opencollective.com/readline/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/18/website" target="_blank"><img src="https://opencollective.com/readline/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/19/website" target="_blank"><img src="https://opencollective.com/readline/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/20/website" target="_blank"><img src="https://opencollective.com/readline/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/21/website" target="_blank"><img src="https://opencollective.com/readline/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/22/website" target="_blank"><img src="https://opencollective.com/readline/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/23/website" target="_blank"><img src="https://opencollective.com/readline/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/24/website" target="_blank"><img src="https://opencollective.com/readline/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/25/website" target="_blank"><img src="https://opencollective.com/readline/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/26/website" target="_blank"><img src="https://opencollective.com/readline/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/27/website" target="_blank"><img src="https://opencollective.com/readline/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/28/website" target="_blank"><img src="https://opencollective.com/readline/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/readline/backer/29/website" target="_blank"><img src="https://opencollective.com/readline/backer/29/avatar.svg"></a>
## Sponsors
Become a sponsor and get your logo here on our Github page. [[Become a sponsor](https://opencollective.com/readline#sponsor)]
<a href="https://opencollective.com/readline/sponsor/0/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/1/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/2/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/3/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/4/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/5/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/6/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/7/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/8/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/9/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/10/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/11/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/12/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/13/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/14/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/15/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/16/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/17/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/18/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/19/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/20/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/21/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/22/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/23/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/24/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/25/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/26/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/27/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/28/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/readline/sponsor/29/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/29/avatar.svg"></a>
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/ansi_windows.go
================================================
// +build windows
package readline
import (
"bufio"
"io"
"strconv"
"strings"
"sync"
"unicode/utf8"
"unsafe"
)
const (
_ = uint16(0)
COLOR_FBLUE = 0x0001
COLOR_FGREEN = 0x0002
COLOR_FRED = 0x0004
COLOR_FINTENSITY = 0x0008
COLOR_BBLUE = 0x0010
COLOR_BGREEN = 0x0020
COLOR_BRED = 0x0040
COLOR_BINTENSITY = 0x0080
COMMON_LVB_UNDERSCORE = 0x8000
)
var ColorTableFg = []word{
0, // 30: Black
COLOR_FRED, // 31: Red
COLOR_FGREEN, // 32: Green
COLOR_FRED | COLOR_FGREEN, // 33: Yellow
COLOR_FBLUE, // 34: Blue
COLOR_FRED | COLOR_FBLUE, // 35: Magenta
COLOR_FGREEN | COLOR_FBLUE, // 36: Cyan
COLOR_FRED | COLOR_FBLUE | COLOR_FGREEN, // 37: White
}
var ColorTableBg = []word{
0, // 40: Black
COLOR_BRED, // 41: Red
COLOR_BGREEN, // 42: Green
COLOR_BRED | COLOR_BGREEN, // 43: Yellow
COLOR_BBLUE, // 44: Blue
COLOR_BRED | COLOR_BBLUE, // 45: Magenta
COLOR_BGREEN | COLOR_BBLUE, // 46: Cyan
COLOR_BRED | COLOR_BBLUE | COLOR_BGREEN, // 47: White
}
type ANSIWriter struct {
target io.Writer
wg sync.WaitGroup
ctx *ANSIWriterCtx
sync.Mutex
}
func NewANSIWriter(w io.Writer) *ANSIWriter {
a := &ANSIWriter{
target: w,
ctx: NewANSIWriterCtx(w),
}
return a
}
func (a *ANSIWriter) Close() error {
a.wg.Wait()
return nil
}
type ANSIWriterCtx struct {
isEsc bool
isEscSeq bool
arg []string
target *bufio.Writer
wantFlush bool
}
func NewANSIWriterCtx(target io.Writer) *ANSIWriterCtx {
return &ANSIWriterCtx{
target: bufio.NewWriter(target),
}
}
func (a *ANSIWriterCtx) Flush() {
a.target.Flush()
}
func (a *ANSIWriterCtx) process(r rune) bool {
if a.wantFlush {
if r == 0 || r == CharEsc {
a.wantFlush = false
a.target.Flush()
}
}
if a.isEscSeq {
a.isEscSeq = a.ioloopEscSeq(a.target, r, &a.arg)
return true
}
switch r {
case CharEsc:
a.isEsc = true
case '[':
if a.isEsc {
a.arg = nil
a.isEscSeq = true
a.isEsc = false
break
}
fallthrough
default:
a.target.WriteRune(r)
a.wantFlush = true
}
return true
}
func (a *ANSIWriterCtx) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) bool {
arg := *argptr
var err error
if r >= 'A' && r <= 'D' {
count := short(GetInt(arg, 1))
info, err := GetConsoleScreenBufferInfo()
if err != nil {
return false
}
switch r {
case 'A': // up
info.dwCursorPosition.y -= count
case 'B': // down
info.dwCursorPosition.y += count
case 'C': // right
info.dwCursorPosition.x += count
case 'D': // left
info.dwCursorPosition.x -= count
}
SetConsoleCursorPosition(&info.dwCursorPosition)
return false
}
switch r {
case 'J':
killLines()
case 'K':
eraseLine()
case 'm':
color := word(0)
for _, item := range arg {
var c int
c, err = strconv.Atoi(item)
if err != nil {
w.WriteString("[" + strings.Join(arg, ";") + "m")
break
}
if c >= 30 && c < 40 {
color ^= COLOR_FINTENSITY
color |= ColorTableFg[c-30]
} else if c >= 40 && c < 50 {
color ^= COLOR_BINTENSITY
color |= ColorTableBg[c-40]
} else if c == 4 {
color |= COMMON_LVB_UNDERSCORE | ColorTableFg[7]
} else { // unknown code treat as reset
color = ColorTableFg[7]
}
}
if err != nil {
break
}
kernel.SetConsoleTextAttribute(stdout, uintptr(color))
case '\007': // set title
case ';':
if len(arg) == 0 || arg[len(arg)-1] != "" {
arg = append(arg, "")
*argptr = arg
}
return true
default:
if len(arg) == 0 {
arg = append(arg, "")
}
arg[len(arg)-1] += string(r)
*argptr = arg
return true
}
*argptr = nil
return false
}
func (a *ANSIWriter) Write(b []byte) (int, error) {
a.Lock()
defer a.Unlock()
off := 0
for len(b) > off {
r, size := utf8.DecodeRune(b[off:])
if size == 0 {
return off, io.ErrShortWrite
}
off += size
a.ctx.process(r)
}
a.ctx.Flush()
return off, nil
}
func killLines() error {
sbi, err := GetConsoleScreenBufferInfo()
if err != nil {
return err
}
size := (sbi.dwCursorPosition.y - sbi.dwSize.y) * sbi.dwSize.x
size += sbi.dwCursorPosition.x
var written int
kernel.FillConsoleOutputAttribute(stdout, uintptr(ColorTableFg[7]),
uintptr(size),
sbi.dwCursorPosition.ptr(),
uintptr(unsafe.Pointer(&written)),
)
return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
uintptr(size),
sbi.dwCursorPosition.ptr(),
uintptr(unsafe.Pointer(&written)),
)
}
func eraseLine() error {
sbi, err := GetConsoleScreenBufferInfo()
if err != nil {
return err
}
size := sbi.dwSize.x
sbi.dwCursorPosition.x = 0
var written int
return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
uintptr(size),
sbi.dwCursorPosition.ptr(),
uintptr(unsafe.Pointer(&written)),
)
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/complete.go
================================================
package readline
import (
"bufio"
"bytes"
"fmt"
"io"
)
type AutoCompleter interface {
// Readline will pass the whole line and current offset to it
// Completer need to pass all the candidates, and how long they shared the same characters in line
// Example:
// [go, git, git-shell, grep]
// Do("g", 1) => ["o", "it", "it-shell", "rep"], 1
// Do("gi", 2) => ["t", "t-shell"], 2
// Do("git", 3) => ["", "-shell"], 3
Do(line []rune, pos int) (newLine [][]rune, length int)
}
type TabCompleter struct{}
func (t *TabCompleter) Do([]rune, int) ([][]rune, int) {
return [][]rune{[]rune("\t")}, 0
}
type opCompleter struct {
w io.Writer
op *Operation
width int
inCompleteMode bool
inSelectMode bool
candidate [][]rune
candidateSource []rune
candidateOff int
candidateChoise int
candidateColNum int
}
func newOpCompleter(w io.Writer, op *Operation, width int) *opCompleter {
return &opCompleter{
w: w,
op: op,
width: width,
}
}
func (o *opCompleter) doSelect() {
if len(o.candidate) == 1 {
o.op.buf.WriteRunes(o.candidate[0])
o.ExitCompleteMode(false)
return
}
o.nextCandidate(1)
o.CompleteRefresh()
}
func (o *opCompleter) nextCandidate(i int) {
o.candidateChoise += i
o.candidateChoise = o.candidateChoise % len(o.candidate)
if o.candidateChoise < 0 {
o.candidateChoise = len(o.candidate) + o.candidateChoise
}
}
func (o *opCompleter) OnComplete() bool {
if o.width == 0 {
return false
}
if o.IsInCompleteSelectMode() {
o.doSelect()
return true
}
buf := o.op.buf
rs := buf.Runes()
if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) {
o.EnterCompleteSelectMode()
o.doSelect()
return true
}
o.ExitCompleteSelectMode()
o.candidateSource = rs
newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)
if len(newLines) == 0 {
o.ExitCompleteMode(false)
return true
}
// only Aggregate candidates in non-complete mode
if !o.IsInCompleteMode() {
if len(newLines) == 1 {
if offset > 0 {
buf.Set([]rune(string(buf.Runes())[0:offset] + string(newLines[0])))
} else {
buf.WriteRunes(newLines[0])
}
o.ExitCompleteMode(false)
return true
}
same, size := runes.Aggregate(newLines)
if size > 0 {
buf.WriteRunes(same)
o.ExitCompleteMode(false)
return true
}
}
o.EnterCompleteMode(offset, newLines)
return true
}
func (o *opCompleter) IsInCompleteSelectMode() bool {
return o.inSelectMode
}
func (o *opCompleter) IsInCompleteMode() bool {
return o.inCompleteMode
}
func (o *opCompleter) HandleCompleteSelect(r rune) bool {
next := true
switch r {
case CharEnter, CharCtrlJ:
next = false
o.op.buf.WriteRunes(o.op.candidate[o.op.candidateChoise])
o.ExitCompleteMode(false)
case CharLineStart:
num := o.candidateChoise % o.candidateColNum
o.nextCandidate(-num)
case CharLineEnd:
num := o.candidateColNum - o.candidateChoise%o.candidateColNum - 1
o.candidateChoise += num
if o.candidateChoise >= len(o.candidate) {
o.candidateChoise = len(o.candidate) - 1
}
case CharBackspace:
o.ExitCompleteSelectMode()
next = false
case CharTab, CharForward:
o.doSelect()
case CharBell, CharInterrupt:
o.ExitCompleteMode(true)
next = false
case CharNext:
tmpChoise := o.candidateChoise + o.candidateColNum
if tmpChoise >= o.getMatrixSize() {
tmpChoise -= o.getMatrixSize()
} else if tmpChoise >= len(o.candidate) {
tmpChoise += o.candidateColNum
tmpChoise -= o.getMatrixSize()
}
o.candidateChoise = tmpChoise
case CharBackward:
o.nextCandidate(-1)
case CharPrev:
tmpChoise := o.candidateChoise - o.candidateColNum
if tmpChoise < 0 {
tmpChoise += o.getMatrixSize()
if tmpChoise >= len(o.candidate) {
tmpChoise -= o.candidateColNum
}
}
o.candidateChoise = tmpChoise
default:
next = false
o.ExitCompleteSelectMode()
}
if next {
o.CompleteRefresh()
return true
}
return false
}
func (o *opCompleter) getMatrixSize() int {
line := len(o.candidate) / o.candidateColNum
if len(o.candidate)%o.candidateColNum != 0 {
line++
}
return line * o.candidateColNum
}
func (o *opCompleter) OnWidthChange(newWidth int) {
o.width = newWidth
}
func (o *opCompleter) CompleteRefresh() {
if !o.inCompleteMode {
return
}
lineCnt := o.op.buf.CursorLineCount()
colWidth := 0
for _, c := range o.candidate {
w := runes.WidthAll(c)
if w > colWidth {
colWidth = w
}
}
colWidth += o.candidateOff + 1
same := o.op.buf.RuneSlice(-o.candidateOff)
// -1 to avoid reach the end of line
width := o.width - 1
colNum := width / colWidth
colWidth += (width - (colWidth * colNum)) / colNum
o.candidateColNum = colNum
buf := bufio.NewWriter(o.w)
buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
colIdx := 0
lines := 1
buf.WriteString("\033[J")
for idx, c := range o.candidate {
inSelect := idx == o.candidateChoise && o.IsInCompleteSelectMode()
if inSelect {
buf.WriteString("\033[30;47m")
}
buf.WriteString(string(same))
buf.WriteString(string(c))
buf.Write(bytes.Repeat([]byte(" "), colWidth-len(c)-len(same)))
if inSelect {
buf.WriteString("\033[0m")
}
colIdx++
if colIdx == colNum {
buf.WriteString("\n")
lines++
colIdx = 0
}
}
// move back
fmt.Fprintf(buf, "\033[%dA\r", lineCnt-1+lines)
fmt.Fprintf(buf, "\033[%dC", o.op.buf.idx+o.op.buf.PromptLen())
buf.Flush()
}
func (o *opCompleter) aggCandidate(candidate [][]rune) int {
offset := 0
for i := 0; i < len(candidate[0]); i++ {
for j := 0; j < len(candidate)-1; j++ {
if i > len(candidate[j]) {
goto aggregate
}
if candidate[j][i] != candidate[j+1][i] {
goto aggregate
}
}
offset = i
}
aggregate:
return offset
}
func (o *opCompleter) EnterCompleteSelectMode() {
o.inSelectMode = true
o.candidateChoise = -1
o.CompleteRefresh()
}
func (o *opCompleter) EnterCompleteMode(offset int, candidate [][]rune) {
o.inCompleteMode = true
o.candidate = candidate
o.candidateOff = offset
o.CompleteRefresh()
}
func (o *opCompleter) ExitCompleteSelectMode() {
o.inSelectMode = false
o.candidate = nil
o.candidateChoise = -1
o.candidateOff = -1
o.candidateSource = nil
}
func (o *opCompleter) ExitCompleteMode(revent bool) {
o.inCompleteMode = false
o.ExitCompleteSelectMode()
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/complete_helper.go
================================================
package readline
import (
"bytes"
"strings"
)
// Caller type for dynamic completion
type DynamicCompleteFunc func(string) []string
type PrefixCompleterInterface interface {
Print(prefix string, level int, buf *bytes.Buffer)
Do(line []rune, pos int) (newLine [][]rune, length int)
GetName() []rune
GetChildren() []PrefixCompleterInterface
SetChildren(children []PrefixCompleterInterface)
}
type DynamicPrefixCompleterInterface interface {
PrefixCompleterInterface
IsDynamic() bool
GetDynamicNames(line []rune) [][]rune
}
type PrefixCompleter struct {
Name []rune
Dynamic bool
Callback DynamicCompleteFunc
Children []PrefixCompleterInterface
}
func (p *PrefixCompleter) Tree(prefix string) string {
buf := bytes.NewBuffer(nil)
p.Print(prefix, 0, buf)
return buf.String()
}
func Print(p PrefixCompleterInterface, prefix string, level int, buf *bytes.Buffer) {
if strings.TrimSpace(string(p.GetName())) != "" {
buf.WriteString(prefix)
if level > 0 {
buf.WriteString("├")
buf.WriteString(strings.Repeat("─", (level*4)-2))
buf.WriteString(" ")
}
buf.WriteString(string(p.GetName()) + "\n")
level++
}
for _, ch := range p.GetChildren() {
ch.Print(prefix, level, buf)
}
}
func (p *PrefixCompleter) Print(prefix string, level int, buf *bytes.Buffer) {
Print(p, prefix, level, buf)
}
func (p *PrefixCompleter) IsDynamic() bool {
return p.Dynamic
}
func (p *PrefixCompleter) GetName() []rune {
return p.Name
}
func (p *PrefixCompleter) GetDynamicNames(line []rune) [][]rune {
var names = [][]rune{}
for _, name := range p.Callback(string(line)) {
names = append(names, []rune(name+" "))
}
return names
}
func (p *PrefixCompleter) GetChildren() []PrefixCompleterInterface {
return p.Children
}
func (p *PrefixCompleter) SetChildren(children []PrefixCompleterInterface) {
p.Children = children
}
func NewPrefixCompleter(pc ...PrefixCompleterInterface) *PrefixCompleter {
return PcItem("", pc...)
}
func PcItem(name string, pc ...PrefixCompleterInterface) *PrefixCompleter {
name += " "
return &PrefixCompleter{
Name: []rune(name),
Dynamic: false,
Children: pc,
}
}
func PcItemDynamic(callback DynamicCompleteFunc, pc ...PrefixCompleterInterface) *PrefixCompleter {
return &PrefixCompleter{
Callback: callback,
Dynamic: true,
Children: pc,
}
}
func (p *PrefixCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) {
return doInternal(p, line, pos, line)
}
func Do(p PrefixCompleterInterface, line []rune, pos int) (newLine [][]rune, offset int) {
return doInternal(p, line, pos, line)
}
func doInternal(p PrefixCompleterInterface, line []rune, pos int, origLine []rune) (newLine [][]rune, offset int) {
line = runes.TrimSpaceLeft(line[:pos])
goNext := false
var lineCompleter PrefixCompleterInterface
for _, child := range p.GetChildren() {
childNames := make([][]rune, 1)
childDynamic, ok := child.(DynamicPrefixCompleterInterface)
if ok && childDynamic.IsDynamic() {
childNames = childDynamic.GetDynamicNames(origLine)
} else {
childNames[0] = child.GetName()
}
for _, childName := range childNames {
if len(line) >= len(childName) {
if runes.HasPrefix(line, childName) {
if len(line) == len(childName) {
newLine = append(newLine, []rune{' '})
} else {
newLine = append(newLine, childName)
}
offset = len(childName)
lineCompleter = child
goNext = true
}
} else {
if runes.HasPrefix(childName, line) {
newLine = append(newLine, childName[len(line):])
offset = len(line)
lineCompleter = child
}
}
}
}
if len(newLine) != 1 {
return
}
tmpLine := make([]rune, 0, len(line))
for i := offset; i < len(line); i++ {
if line[i] == ' ' {
continue
}
tmpLine = append(tmpLine, line[i:]...)
return doInternal(lineCompleter, tmpLine, len(tmpLine), origLine)
}
if goNext {
return doInternal(lineCompleter, nil, 0, origLine)
}
return
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/complete_segment.go
================================================
package readline
type SegmentCompleter interface {
// a
// |- a1
// |--- a11
// |- a2
// b
// input:
// DoTree([], 0) [a, b]
// DoTree([a], 1) [a]
// DoTree([a, ], 0) [a1, a2]
// DoTree([a, a], 1) [a1, a2]
// DoTree([a, a1], 2) [a1]
// DoTree([a, a1, ], 0) [a11]
// DoTree([a, a1, a], 1) [a11]
DoSegment([][]rune, int) [][]rune
}
type dumpSegmentCompleter struct {
f func([][]rune, int) [][]rune
}
func (d *dumpSegmentCompleter) DoSegment(segment [][]rune, n int) [][]rune {
return d.f(segment, n)
}
func SegmentFunc(f func([][]rune, int) [][]rune) AutoCompleter {
return &SegmentComplete{&dumpSegmentCompleter{f}}
}
func SegmentAutoComplete(completer SegmentCompleter) *SegmentComplete {
return &SegmentComplete{
SegmentCompleter: completer,
}
}
type SegmentComplete struct {
SegmentCompleter
}
func RetSegment(segments [][]rune, cands [][]rune, idx int) ([][]rune, int) {
ret := make([][]rune, 0, len(cands))
lastSegment := segments[len(segments)-1]
for _, cand := range cands {
if !runes.HasPrefix(cand, lastSegment) {
continue
}
ret = append(ret, cand[len(lastSegment):])
}
return ret, idx
}
func SplitSegment(line []rune, pos int) ([][]rune, int) {
segs := [][]rune{}
lastIdx := -1
line = line[:pos]
pos = 0
for idx, l := range line {
if l == ' ' {
pos = 0
segs = append(segs, line[lastIdx+1:idx])
lastIdx = idx
} else {
pos++
}
}
segs = append(segs, line[lastIdx+1:])
return segs, pos
}
func (c *SegmentComplete) Do(line []rune, pos int) (newLine [][]rune, offset int) {
segment, idx := SplitSegment(line, pos)
cands := c.DoSegment(segment, idx)
newLine, offset = RetSegment(segment, cands, idx)
for idx := range newLine {
newLine[idx] = append(newLine[idx], ' ')
}
return newLine, offset
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/complete_segment_test.go
================================================
package readline
import (
"fmt"
"testing"
"github.com/chzyer/test"
)
func rs(s [][]rune) []string {
ret := make([]string, len(s))
for idx, ss := range s {
ret[idx] = string(ss)
}
return ret
}
func sr(s ...string) [][]rune {
ret := make([][]rune, len(s))
for idx, ss := range s {
ret[idx] = []rune(ss)
}
return ret
}
func TestRetSegment(t *testing.T) {
defer test.New(t)
// a
// |- a1
// |--- a11
// |--- a12
// |- a2
// |--- a21
// b
// add
// adddomain
ret := []struct {
Segments [][]rune
Cands [][]rune
idx int
Ret [][]rune
pos int
}{
{sr(""), sr("a", "b", "add", "adddomain"), 0, sr("a", "b", "add", "adddomain"), 0},
{sr("a"), sr("a", "add", "adddomain"), 1, sr("", "dd", "dddomain"), 1},
{sr("a", ""), sr("a1", "a2"), 0, sr("a1", "a2"), 0},
{sr("a", "a"), sr("a1", "a2"), 1, sr("1", "2"), 1},
{sr("a", "a1"), sr("a1"), 2, sr(""), 2},
{sr("add"), sr("add", "adddomain"), 2, sr("", "domain"), 2},
}
for idx, r := range ret {
ret, pos := RetSegment(r.Segments, r.Cands, r.idx)
test.Equal(ret, r.Ret, fmt.Errorf("%v", idx))
test.Equal(pos, r.pos, fmt.Errorf("%v", idx))
}
}
func TestSplitSegment(t *testing.T) {
defer test.New(t)
// a
// |- a1
// |--- a11
// |--- a12
// |- a2
// |--- a21
// b
ret := []struct {
Line string
Pos int
Segments [][]rune
Idx int
}{
{"", 0, sr(""), 0},
{"a", 1, sr("a"), 1},
{"a ", 2, sr("a", ""), 0},
{"a a", 3, sr("a", "a"), 1},
{"a a1", 4, sr("a", "a1"), 2},
{"a a1 ", 5, sr("a", "a1", ""), 0},
}
for i, r := range ret {
ret, idx := SplitSegment([]rune(r.Line), r.Pos)
test.Equal(rs(ret), rs(r.Segments), fmt.Errorf("%v", i))
test.Equal(idx, r.Idx, fmt.Errorf("%v", i))
}
}
type Tree struct {
Name string
Children []Tree
}
func TestSegmentCompleter(t *testing.T) {
defer test.New(t)
tree := Tree{"", []Tree{
{"a", []Tree{
{"a1", []Tree{
{"a11", nil},
{"a12", nil},
}},
{"a2", []Tree{
{"a21", nil},
}},
}},
{"b", nil},
{"route", []Tree{
{"add", nil},
{"adddomain", nil},
}},
}}
s := SegmentFunc(func(ret [][]rune, n int) [][]rune {
tree := tree
main:
for level := 0; level < len(ret)-1; {
name := string(ret[level])
for _, t := range tree.Children {
if t.Name == name {
tree = t
level++
continue main
}
}
}
ret = make([][]rune, len(tree.Children))
for idx, r := range tree.Children {
ret[idx] = []rune(r.Name)
}
return ret
})
// a
// |- a1
// |--- a11
// |--- a12
// |- a2
// |--- a21
// b
ret := []struct {
Line string
Pos int
Ret [][]rune
Share int
}{
{"", 0, sr("a", "b", "route"), 0},
{"a", 1, sr(""), 1},
{"a ", 2, sr("a1", "a2"), 0},
{"a a", 3, sr("1", "2"), 1},
{"a a1", 4, sr(""), 2},
{"a a1 ", 5, sr("a11", "a12"), 0},
{"a a1 a", 6, sr("11", "12"), 1},
{"a a1 a1", 7, sr("1", "2"), 2},
{"a a1 a11", 8, sr(""), 3},
{"route add", 9, sr("", "domain"), 3},
}
for _, r := range ret {
for idx, rr := range r.Ret {
r.Ret[idx] = append(rr, ' ')
}
}
for i, r := range ret {
newLine, length := s.Do([]rune(r.Line), r.Pos)
test.Equal(rs(newLine), rs(r.Ret), fmt.Errorf("%v", i))
test.Equal(length, r.Share, fmt.Errorf("%v", i))
}
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/doc/shortcut.md
================================================
## Readline Shortcut
`Meta`+`B` means press `Esc` and `n` separately.
Users can change that in terminal simulator(i.e. iTerm2) to `Alt`+`B`
Notice: `Meta`+`B` is equals with `Alt`+`B` in windows.
* Shortcut in normal mode
| Shortcut | Comment |
| ------------------ | --------------------------------- |
| `Ctrl`+`A` | Beginning of line |
| `Ctrl`+`B` / `←` | Backward one character |
| `Meta`+`B` | Backward one word |
| `Ctrl`+`C` | Send io.EOF |
| `Ctrl`+`D` | Delete one character |
| `Meta`+`D` | Delete one word |
| `Ctrl`+`E` | End of line |
| `Ctrl`+`F` / `→` | Forward one character |
| `Meta`+`F` | Forward one word |
| `Ctrl`+`G` | Cancel |
| `Ctrl`+`H` | Delete previous character |
| `Ctrl`+`I` / `Tab` | Command line completion |
| `Ctrl`+`J` | Line feed |
| `Ctrl`+`K` | Cut text to the end of line |
| `Ctrl`+`L` | Clear screen |
| `Ctrl`+`M` | Same as Enter key |
| `Ctrl`+`N` / `↓` | Next line (in history) |
| `Ctrl`+`P` / `↑` | Prev line (in history) |
| `Ctrl`+`R` | Search backwards in history |
| `Ctrl`+`S` | Search forwards in history |
| `Ctrl`+`T` | Transpose characters |
| `Meta`+`T` | Transpose words (TODO) |
| `Ctrl`+`U` | Cut text to the beginning of line |
| `Ctrl`+`W` | Cut previous word |
| `Backspace` | Delete previous character |
| `Meta`+`Backspace` | Cut previous word |
| `Enter` | Line feed |
* Shortcut in Search Mode (`Ctrl`+`S` or `Ctrl`+`r` to enter this mode)
| Shortcut | Comment |
| ----------------------- | --------------------------------------- |
| `Ctrl`+`S` | Search forwards in history |
| `Ctrl`+`R` | Search backwards in history |
| `Ctrl`+`C` / `Ctrl`+`G` | Exit Search Mode and revert the history |
| `Backspace` | Delete previous character |
| Other | Exit Search Mode |
* Shortcut in Complete Select Mode (double `Tab` to enter this mode)
| Shortcut | Comment |
| ----------------------- | ---------------------------------------- |
| `Ctrl`+`F` | Move Forward |
| `Ctrl`+`B` | Move Backward |
| `Ctrl`+`N` | Move to next line |
| `Ctrl`+`P` | Move to previous line |
| `Ctrl`+`A` | Move to the first candicate in current line |
| `Ctrl`+`E` | Move to the last candicate in current line |
| `Tab` / `Enter` | Use the word on cursor to complete |
| `Ctrl`+`C` / `Ctrl`+`G` | Exit Complete Select Mode |
| Other | Exit Complete Select Mode |
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-demo/readline-demo.go
================================================
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"strconv"
"strings"
"time"
"github.com/chzyer/readline"
)
func usage(w io.Writer) {
io.WriteString(w, "commands:\n")
io.WriteString(w, completer.Tree(" "))
}
// Function constructor - constructs new function for listing given directory
func listFiles(path string) func(string) []string {
return func(line string) []string {
names := make([]string, 0)
files, _ := ioutil.ReadDir(path)
for _, f := range files {
names = append(names, f.Name())
}
return names
}
}
var completer = readline.NewPrefixCompleter(
readline.PcItem("mode",
readline.PcItem("vi"),
readline.PcItem("emacs"),
),
readline.PcItem("login"),
readline.PcItem("say",
readline.PcItemDynamic(listFiles("./"),
readline.PcItem("with",
readline.PcItem("following"),
readline.PcItem("items"),
),
),
readline.PcItem("hello"),
readline.PcItem("bye"),
),
readline.PcItem("setprompt"),
readline.PcItem("setpassword"),
readline.PcItem("bye"),
readline.PcItem("help"),
readline.PcItem("go",
readline.PcItem("build", readline.PcItem("-o"), readline.PcItem("-v")),
readline.PcItem("install",
readline.PcItem("-v"),
readline.PcItem("-vv"),
readline.PcItem("-vvv"),
),
readline.PcItem("test"),
),
readline.PcItem("sleep"),
)
func main() {
l, err := readline.NewEx(&readline.Config{
Prompt: "\033[31m»\033[0m ",
HistoryFile: "/tmp/readline.tmp",
AutoComplete: completer,
InterruptPrompt: "^C",
EOFPrompt: "exit",
HistorySearchFold: true,
})
if err != nil {
panic(err)
}
defer l.Close()
setPasswordCfg := l.GenPasswordConfig()
setPasswordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
l.SetPrompt(fmt.Sprintf("Enter password(%v): ", len(line)))
l.Refresh()
return nil, 0, false
})
log.SetOutput(l.Stderr())
for {
line, err := l.Readline()
if err == readline.ErrInterrupt {
if len(line) == 0 {
break
} else {
continue
}
} else if err == io.EOF {
break
}
line = strings.TrimSpace(line)
switch {
case strings.HasPrefix(line, "mode "):
switch line[5:] {
case "vi":
l.SetVimMode(true)
case "emacs":
l.SetVimMode(false)
default:
println("invalid mode:", line[5:])
}
case line == "mode":
if l.IsVimMode() {
println("current mode: vim")
} else {
println("current mode: emacs")
}
case line == "login":
pswd, err := l.ReadPassword("please enter your password: ")
if err != nil {
break
}
println("you enter:", strconv.Quote(string(pswd)))
case line == "help":
usage(l.Stderr())
case line == "setpassword":
pswd, err := l.ReadPasswordWithConfig(setPasswordCfg)
if err == nil {
println("you set:", strconv.Quote(string(pswd)))
}
case strings.HasPrefix(line, "setprompt"):
prompt := line[10:]
if prompt == "" {
log.Println("setprompt <prompt>")
break
}
l.SetPrompt(prompt)
case strings.HasPrefix(line, "say"):
line := strings.TrimSpace(line[3:])
if len(line) == 0 {
log.Println("say what?")
break
}
go func() {
for range time.Tick(time.Second) {
log.Println(line)
}
}()
case line == "bye":
goto exit
case line == "sleep":
log.Println("sleep 4 second")
time.Sleep(4 * time.Second)
case line == "":
default:
log.Println("you said:", strconv.Quote(line))
}
}
exit:
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-im/README.md
================================================
# readline-im

================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-im/readline-im.go
================================================
package main
import (
"fmt"
"math/rand"
"time"
"github.com/chzyer/readline"
)
import "log"
func main() {
rl, err := readline.NewEx(&readline.Config{
UniqueEditLine: true,
})
if err != nil {
panic(err)
}
defer rl.Close()
rl.SetPrompt("username: ")
username, err := rl.Readline()
if err != nil {
return
}
rl.ResetHistory()
log.SetOutput(rl.Stderr())
fmt.Fprintln(rl, "Hi,", username+"! My name is Dave.")
rl.SetPrompt(username + "> ")
done := make(chan struct{})
go func() {
rand.Seed(time.Now().Unix())
loop:
for {
select {
case <-time.After(time.Duration(rand.Intn(20)) * 100 * time.Millisecond):
case <-done:
break loop
}
log.Println("Dave:", "hello")
}
log.Println("Dave:", "bye")
done <- struct{}{}
}()
for {
ln := rl.Line()
if ln.CanContinue() {
continue
} else if ln.CanBreak() {
break
}
log.Println(username+":", ln.Line)
}
rl.Clean()
done <- struct{}{}
<-done
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-multiline/readline-multiline.go
================================================
package main
import (
"strings"
"github.com/chzyer/readline"
)
func main() {
rl, err := readline.NewEx(&readline.Config{
Prompt: "> ",
HistoryFile: "/tmp/readline-multiline",
DisableAutoSaveHistory: true,
})
if err != nil {
panic(err)
}
defer rl.Close()
var cmds []string
for {
line, err := rl.Readline()
if err != nil {
break
}
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
cmds = append(cmds, line)
if !strings.HasSuffix(line, ";") {
rl.SetPrompt(">>> ")
continue
}
cmd := strings.Join(cmds, " ")
cmds = cmds[:0]
rl.SetPrompt("> ")
rl.SaveHistory(cmd)
println(cmd)
}
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-pass-strength/readline-pass-strength.go
================================================
// This is a small example using readline to read a password
// and check it's strength while typing using the zxcvbn library.
// Depending on the strength the prompt is colored nicely to indicate strength.
//
// This file is licensed under the WTFPL:
//
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// Version 2, December 2004
//
// Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
//
// Everyone is permitted to copy and distribute verbatim or modified
// copies of this license document, and changing it is allowed as long
// as the name is changed.
//
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
//
// 0. You just DO WHAT THE FUCK YOU WANT TO.
package main
import (
"fmt"
"github.com/chzyer/readline"
zxcvbn "github.com/nbutton23/zxcvbn-go"
)
const (
Cyan = 36
Green = 32
Magenta = 35
Red = 31
Yellow = 33
BackgroundRed = 41
)
// Reset sequence
var ColorResetEscape = "\033[0m"
// ColorResetEscape translates a ANSI color number to a color escape.
func ColorEscape(color int) string {
return fmt.Sprintf("\033[0;%dm", color)
}
// Colorize the msg using ANSI color escapes
func Colorize(msg string, color int) string {
return ColorEscape(color) + msg + ColorResetEscape
}
func createStrengthPrompt(password []rune) string {
symbol, color := "", Red
strength := zxcvbn.PasswordStrength(string(password), nil)
switch {
case strength.Score <= 1:
symbol = "✗"
color = Red
case strength.Score <= 2:
symbol = "⚡"
color = Magenta
case strength.Score <= 3:
symbol = "⚠"
color = Yellow
case strength.Score <= 4:
symbol = "✔"
color = Green
}
prompt := Colorize(symbol, color)
if strength.Entropy > 0 {
entropy := fmt.Sprintf(" %3.0f", strength.Entropy)
prompt += Colorize(entropy, Cyan)
} else {
prompt += Colorize(" ENT", Cyan)
}
prompt += Colorize(" New Password: ", color)
return prompt
}
func main() {
rl, err := readline.New("")
if err != nil {
return
}
defer rl.Close()
setPasswordCfg := rl.GenPasswordConfig()
setPasswordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
rl.SetPrompt(createStrengthPrompt(line))
rl.Refresh()
return nil, 0, false
})
pswd, err := rl.ReadPasswordWithConfig(setPasswordCfg)
if err != nil {
return
}
fmt.Println("Your password was:", string(pswd))
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-remote/readline-remote-client/client.go
================================================
package main
import "github.com/chzyer/readline"
func main() {
if err := readline.DialRemote("tcp", ":12344"); err != nil {
println(err.Error())
}
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-remote/readline-remote-server/server.go
================================================
package main
import (
"fmt"
"github.com/chzyer/readline"
)
func main() {
cfg := &readline.Config{
Prompt: "readline-remote: ",
}
handleFunc := func(rl *readline.Instance) {
for {
line, err := rl.Readline()
if err != nil {
break
}
fmt.Fprintln(rl.Stdout(), "receive:"+line)
}
}
err := readline.ListenRemote("tcp", ":12344", cfg, handleFunc)
if err != nil {
println(err.Error())
}
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/history.go
================================================
package readline
import (
"bufio"
"container/list"
"fmt"
"os"
"strings"
"sync"
)
type hisItem struct {
Source []rune
Version int64
Tmp []rune
}
func (h *hisItem) Clean() {
h.Source = nil
h.Tmp = nil
}
type opHistory struct {
cfg *Config
history *list.List
historyVer int64
current *list.Element
fd *os.File
fdLock sync.Mutex
}
func newOpHistory(cfg *Config) (o *opHistory) {
o = &opHistory{
cfg: cfg,
history: list.New(),
}
return o
}
func (o *opHistory) Reset() {
o.history = list.New()
o.current = nil
}
func (o *opHistory) IsHistoryClosed() bool {
o.fdLock.Lock()
defer o.fdLock.Unlock()
return o.fd.Fd() == ^(uintptr(0))
}
func (o *opHistory) Init() {
if o.IsHistoryClosed() {
o.initHistory()
}
}
func (o *opHistory) initHistory() {
if o.cfg.HistoryFile != "" {
o.historyUpdatePath(o.cfg.HistoryFile)
}
}
// only called by newOpHistory
func (o *opHistory) historyUpdatePath(path string) {
o.fdLock.Lock()
defer o.fdLock.Unlock()
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return
}
o.fd = f
r := bufio.NewReader(o.fd)
total := 0
for ; ; total++ {
line, err := r.ReadString('\n')
if err != nil {
break
}
// ignore the empty line
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
o.Push([]rune(line))
o.Compact()
}
if total > o.cfg.HistoryLimit {
o.rewriteLocked()
}
o.historyVer++
o.Push(nil)
return
}
func (o *opHistory) Compact() {
for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 {
o.history.Remove(o.history.Front())
}
}
func (o *opHistory) Rewrite() {
o.fdLock.Lock()
defer o.fdLock.Unlock()
o.rewriteLocked()
}
func (o *opHistory) rewriteLocked() {
if o.cfg.HistoryFile == "" {
return
}
tmpFile := o.cfg.HistoryFile + ".tmp"
fd, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_APPEND, 0666)
if err != nil {
return
}
buf := bufio.NewWriter(fd)
for elem := o.history.Front(); elem != nil; elem = elem.Next() {
buf.WriteString(string(elem.Value.(*hisItem).Source))
}
buf.Flush()
// replace history file
if err = os.Rename(tmpFile, o.cfg.HistoryFile); err != nil {
fd.Close()
return
}
if o.fd != nil {
o.fd.Close()
}
// fd is write only, just satisfy what we need.
o.fd = fd
}
func (o *opHistory) Close() {
o.fdLock.Lock()
defer o.fdLock.Unlock()
if o.fd != nil {
o.fd.Close()
}
}
func (o *opHistory) FindBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
for elem := o.current; elem != nil; elem = elem.Prev() {
item := o.showItem(elem.Value)
if isNewSearch {
start += len(rs)
}
if elem == o.current {
if len(item) >= start {
item = item[:start]
}
}
idx := runes.IndexAllBckEx(item, rs, o.cfg.HistorySearchFold)
if idx < 0 {
continue
}
return idx, elem
}
return -1, nil
}
func (o *opHistory) FindFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
for elem := o.current; elem != nil; elem = elem.Next() {
item := o.showItem(elem.Value)
if isNewSearch {
start -= len(rs)
if start < 0 {
start = 0
}
}
if elem == o.current {
if len(item)-1 >= start {
item = item[start:]
} else {
continue
}
}
idx := runes.IndexAllEx(item, rs, o.cfg.HistorySearchFold)
if idx < 0 {
continue
}
if elem == o.current {
idx += start
}
return idx, elem
}
return -1, nil
}
func (o *opHistory) showItem(obj interface{}) []rune {
item := obj.(*hisItem)
if item.Version == o.historyVer {
return item.Tmp
}
return item.Source
}
func (o *opHistory) Prev() []rune {
if o.current == nil {
return nil
}
current := o.current.Prev()
if current == nil {
return nil
}
o.current = current
return runes.Copy(o.showItem(current.Value))
}
func (o *opHistory) Next() ([]rune, bool) {
if o.current == nil {
return nil, false
}
current := o.current.Next()
if current == nil {
return nil, false
}
o.current = current
return runes.Copy(o.showItem(current.Value)), true
}
func (o *opHistory) debug() {
Debug("-------")
for item := o.history.Front(); item != nil; item = item.Next() {
Debug(fmt.Sprintf("%+v", item.Value))
}
}
// save history
func (o *opHistory) New(current []rune) (err error) {
current = runes.Copy(current)
// if just use last command without modify
// just clean lastest history
if back := o.history.Back(); back != nil {
prev := back.Prev()
if prev != nil {
if runes.Equal(current, prev.Value.(*hisItem).Source) {
o.current = o.history.Back()
o.current.Value.(*hisItem).Clean()
o.historyVer++
return nil
}
}
}
if len(current) == 0 {
o.current = o.history.Back()
if o.current != nil {
o.current.Value.(*hisItem).Clean()
o.historyVer++
return nil
}
}
if o.current != o.history.Back() {
// move history item to current command
currentItem := o.current.Value.(*hisItem)
// set current to last item
o.current = o.history.Back()
current = runes.Copy(currentItem.Tmp)
}
// err only can be a IO error, just report
err = o.Update(current, true)
// push a new one to commit current command
o.historyVer++
o.Push(nil)
return
}
func (o *opHistory) Revert() {
o.historyVer++
o.current = o.history.Back()
}
func (o *opHistory) Update(s []rune, commit bool) (err error) {
o.fdLock.Lock()
defer o.fdLock.Unlock()
s = runes.Copy(s)
if o.current == nil {
o.Push(s)
o.Compact()
return
}
r := o.current.Value.(*hisItem)
r.Version = o.historyVer
if commit {
r.Source = s
if o.fd != nil {
// just report the error
_, err = o.fd.Write([]byte(string(r.Source) + "\n"))
}
} else {
r.Tmp = append(r.Tmp[:0], s...)
}
o.current.Value = r
o.Compact()
return
}
func (o *opHistory) Push(s []rune) {
s = runes.Copy(s)
elem := o.history.PushBack(&hisItem{Source: s})
o.current = elem
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/operation.go
================================================
package readline
import (
"errors"
"io"
)
var (
ErrInterrupt = errors.New("Interrupt")
)
type InterruptError struct {
Line []rune
}
func (*InterruptError) Error() string {
return "Interrupted"
}
type Operation struct {
cfg *Config
t *Terminal
buf *RuneBuffer
outchan chan []rune
errchan chan error
w io.Writer
history *opHistory
*opCompleter
*opPassword
*opVim
}
type wrapWriter struct {
r *Operation
t *Terminal
target io.Writer
}
func (w *wrapWriter) Write(b []byte) (int, error) {
if !w.t.IsReading() {
return w.target.Write(b)
}
var (
n int
err error
)
w.r.buf.Refresh(func() {
n, err = w.target.Write(b)
})
if w.r.IsInCompleteMode() {
w.r.CompleteRefresh()
}
return n, err
}
func NewOperation(t *Terminal, cfg *Config) *Operation {
width := cfg.FuncGetWidth()
op := &Operation{
t: t,
buf: NewRuneBuffer(t, cfg.Prompt, cfg, width),
outchan: make(chan []rune),
errchan: make(chan error),
}
op.w = op.buf.w
op.SetConfig(cfg)
op.opVim = newVimMode(op)
op.opCompleter = newOpCompleter(op.buf.w, op, width)
op.opPassword = newOpPassword(op)
op.cfg.FuncOnWidthChanged(func() {
newWidth := cfg.FuncGetWidth()
op.opCompleter.OnWidthChange(newWidth)
op.buf.OnWidthChange(newWidth)
})
go op.ioloop()
return op
}
func (o *Operation) SetPrompt(s string) {
o.buf.SetPrompt(s)
}
func (o *Operation) SetMaskRune(r rune) {
o.buf.SetMask(r)
}
func (o *Operation) ioloop() {
for {
keepInCompleteMode := false
r := o.t.ReadRune()
if r == 0 { // io.EOF
if o.buf.Len() == 0 {
o.buf.Clean()
select {
case o.errchan <- io.EOF:
}
break
} else {
// if stdin got io.EOF and there is something left in buffer,
// let's flush them by sending CharEnter.
// And we will got io.EOF int next loop.
r = CharEnter
}
}
isUpdateHistory := true
if o.IsInCompleteSelectMode() {
keepInCompleteMode = o.HandleCompleteSelect(r)
if keepInCompleteMode {
continue
}
o.buf.Refresh(nil)
switch r {
case CharEnter, CharCtrlJ:
o.history.Update(o.buf.Runes(), false)
fallthrough
case CharInterrupt:
o.t.KickRead()
fallthrough
case CharBell:
continue
}
}
if o.IsEnableVimMode() {
r = o.HandleVim(r, o.t.ReadRune)
if r == 0 {
continue
}
}
switch r {
case CharBell:
if o.IsInCompleteMode() {
o.ExitCompleteMode(true)
o.buf.Refresh(nil)
}
case CharTab:
if o.cfg.AutoComplete == nil {
o.t.Bell()
break
}
if o.OnComplete() {
keepInCompleteMode = true
} else {
o.t.Bell()
break
}
case CharCtrlU:
o.buf.KillFront()
case CharKill:
o.buf.Kill()
keepInCompleteMode = true
case MetaForward:
o.buf.MoveToNextWord()
case CharTranspose:
o.buf.Transpose()
case MetaBackward:
o.buf.MoveToPrevWord()
case MetaDelete:
o.buf.DeleteWord()
case CharLineStart:
o.buf.MoveToLineStart()
case CharLineEnd:
o.buf.MoveToLineEnd()
case CharBackspace, CharCtrlH:
if o.buf.Len() == 0 {
o.t.Bell()
break
}
o.buf.Backspace()
if o.IsInCompleteMode() {
o.OnComplete()
}
case CharCtrlZ:
o.buf.Clean()
o.t.SleepToResume()
o.Refresh()
case CharCtrlL:
ClearScreen(o.w)
o.Refresh()
case MetaBackspace, CharCtrlW:
o.buf.BackEscapeWord()
case CharEnter, CharCtrlJ:
o.buf.MoveToLineEnd()
var data []rune
if !o.cfg.UniqueEditLine {
o.buf.WriteRune('\n')
data = o.buf.Reset()
data = data[:len(data)-1] // trim \n
} else {
o.buf.Clean()
data = o.buf.Reset()
}
o.outchan <- data
if !o.cfg.DisableAutoSaveHistory {
// ignore IO error
_ = o.history.New(data)
} else {
isUpdateHistory = false
}
case CharBackward:
o.buf.MoveBackward()
case CharForward:
o.buf.MoveForward()
case CharPrev:
buf := o.history.Prev()
if buf != nil {
o.buf.Set(buf)
} else {
o.t.Bell()
}
case CharNext:
buf, ok := o.history.Next()
if ok {
o.buf.Set(buf)
} else {
o.t.Bell()
}
case CharDelete:
if o.buf.Len() > 0 || !o.IsNormalMode() {
o.t.KickRead()
if !o.buf.Delete() {
o.t.Bell()
}
break
}
// treat as EOF
if !o.cfg.UniqueEditLine {
o.buf.WriteString(o.cfg.EOFPrompt + "\n")
}
o.buf.Reset()
isUpdateHistory = false
o.history.Revert()
o.errchan <- io.EOF
if o.cfg.UniqueEditLine {
o.buf.Clean()
}
case CharInterrupt:
if o.IsInCompleteMode() {
o.t.KickRead()
o.ExitCompleteMode(true)
o.buf.Refresh(nil)
break
}
o.buf.MoveToLineEnd()
o.buf.Refresh(nil)
hint := o.cfg.InterruptPrompt + "\n"
if !o.cfg.UniqueEditLine {
o.buf.WriteString(hint)
}
remain := o.buf.Reset()
if !o.cfg.UniqueEditLine {
remain = remain[:len(remain)-len([]rune(hint))]
}
isUpdateHistory = false
o.history.Revert()
o.errchan <- &InterruptError{remain}
default:
o.buf.WriteRune(r)
if o.IsInCompleteMode() {
o.OnComplete()
keepInCompleteMode = true
}
}
if o.cfg.Listener != nil {
newLine, newPos, ok := o.cfg.Listener.OnChange(o.buf.Runes(), o.buf.Pos(), r)
if ok {
o.buf.SetWithIdx(newPos, newLine)
}
}
if o.IsInCompleteMode() {
if !keepInCompleteMode {
o.ExitCompleteMode(false)
o.Refresh()
} else {
o.buf.Refresh(nil)
o.CompleteRefresh()
}
}
if isUpdateHistory {
// it will cause null history
o.history.Update(o.buf.Runes(), false)
}
}
}
func (o *Operation) Stderr() io.Writer {
return &wrapWriter{target: o.cfg.Stderr, r: o, t: o.t}
}
func (o *Operation) Stdout() io.Writer {
return &wrapWriter{target: o.cfg.Stdout, r: o, t: o.t}
}
func (o *Operation) String() (string, error) {
r, err := o.Runes()
return string(r), err
}
func (o *Operation) Runes() ([]rune, error) {
o.t.EnterRawMode()
defer o.t.ExitRawMode()
if o.cfg.Listener != nil {
o.cfg.Listener.OnChange(nil, 0, 0)
}
o.buf.Refresh(nil) // print prompt
o.t.KickRead()
select {
case r := <-o.outchan:
return r, nil
case err := <-o.errchan:
if e, ok := err.(*InterruptError); ok {
return e.Line, ErrInterrupt
}
return nil, err
}
}
func (o *Operation) PasswordEx(prompt string, l Listener) ([]byte, error) {
cfg := o.GenPasswordConfig()
cfg.Prompt = prompt
cfg.Listener = l
return o.PasswordWithConfig(cfg)
}
func (o *Operation) GenPasswordConfig() *Config {
return o.opPassword.PasswordConfig()
}
func (o *Operation) PasswordWithConfig(cfg *Config) ([]byte, error) {
if err := o.opPassword.EnterPasswordMode(cfg); err != nil {
return nil, err
}
defer o.opPassword.ExitPasswordMode()
return o.Slice()
}
func (o *Operation) Password(prompt string) ([]byte, error) {
return o.PasswordEx(prompt, nil)
}
func (o *Operation) SetTitle(t string) {
o.w.Write([]byte("\033[2;" + t + "\007"))
}
func (o *Operation) Slice() ([]byte, error) {
r, err := o.Runes()
if err != nil {
return nil, err
}
return []byte(string(r)), nil
}
func (o *Operation) Close() {
o.history.Close()
}
func (o *Operation) SetHistoryPath(path string) {
if o.history != nil {
o.history.Close()
}
o.cfg.HistoryFile = path
o.history = newOpHistory(o.cfg)
}
func (o *Operation) IsNormalMode() bool {
return !o.IsInCompleteMode()
}
func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
if op.cfg == cfg {
return op.cfg, nil
}
if err := cfg.Init(); err != nil {
return op.cfg, err
}
old := op.cfg
op.cfg = cfg
op.SetPrompt(cfg.Prompt)
op.SetMaskRune(cfg.MaskRune)
op.buf.SetConfig(cfg)
width := op.cfg.FuncGetWidth()
if cfg.opHistory == nil {
op.SetHistoryPath(cfg.HistoryFile)
cfg.opHistory = op.history
}
op.history = cfg.opHistory
// SetHistoryPath will close opHistory which already exists
// so if we use it next time, we need to reopen it by `InitHistory()`
op.history.Init()
if op.cfg.AutoComplete != nil {
op.opCompleter = newOpCompleter(op.buf.w, op, width)
}
return old, nil
}
func (o *Operation) ResetHistory() {
o.history.Reset()
}
// if err is not nil, it just mean it fail to write to file
// other things goes fine.
func (o *Operation) SaveHistory(content string) error {
return o.history.New([]rune(content))
}
func (o *Operation) Refresh() {
if o.t.IsReading() {
o.buf.Refresh(nil)
}
}
func (o *Operation) Clean() {
o.buf.Clean()
}
func FuncListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) Listener {
return &DumpListener{f: f}
}
type DumpListener struct {
f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
}
func (d *DumpListener) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
return d.f(line, pos, key)
}
type Listener interface {
OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/password.go
================================================
package readline
type opPassword struct {
o *Operation
backupCfg *Config
}
func newOpPassword(o *Operation) *opPassword {
return &opPassword{o: o}
}
func (o *opPassword) ExitPasswordMode() {
o.o.SetConfig(o.backupCfg)
o.backupCfg = nil
}
func (o *opPassword) EnterPasswordMode(cfg *Config) (err error) {
o.backupCfg, err = o.o.SetConfig(cfg)
return
}
func (o *opPassword) PasswordConfig() *Config {
return &Config{
EnableMask: true,
InterruptPrompt: "\n",
EOFPrompt: "\n",
HistoryLimit: -1,
Stdout: o.o.cfg.Stdout,
Stderr: o.o.cfg.Stderr,
}
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/rawreader_windows.go
================================================
// +build windows
package readline
import "unsafe"
const (
VK_CANCEL = 0x03
VK_BACK = 0x08
VK_TAB = 0x09
VK_RETURN = 0x0D
VK_SHIFT = 0x10
VK_CONTROL = 0x11
VK_MENU = 0x12
VK_ESCAPE = 0x1B
VK_LEFT = 0x25
VK_UP = 0x26
VK_RIGHT = 0x27
VK_DOWN = 0x28
VK_DELETE = 0x2E
VK_LSHIFT = 0xA0
VK_RSHIFT = 0xA1
VK_LCONTROL = 0xA2
VK_RCONTROL = 0xA3
)
// RawReader translate input record to ANSI escape sequence.
// To provides same behavior as unix terminal.
type RawReader struct {
ctrlKey bool
altKey bool
}
func NewRawReader() *RawReader {
r := new(RawReader)
return r
}
// only process one action in one read
func (r *RawReader) Read(buf []byte) (int, error) {
ir := new(_INPUT_RECORD)
var read int
var err error
next:
err = kernel.ReadConsoleInputW(stdin,
uintptr(unsafe.Pointer(ir)),
1,
uintptr(unsafe.Pointer(&read)),
)
if err != nil {
return 0, err
}
if ir.EventType != EVENT_KEY {
goto next
}
ker := (*_KEY_EVENT_RECORD)(unsafe.Pointer(&ir.Event[0]))
if ker.bKeyDown == 0 { // keyup
if r.ctrlKey || r.altKey {
switch ker.wVirtualKeyCode {
case VK_RCONTROL, VK_LCONTROL:
r.ctrlKey = false
case VK_MENU: //alt
r.altKey = false
}
}
goto next
}
if ker.unicodeChar == 0 {
var target rune
switch ker.wVirtualKeyCode {
case VK_RCONTROL, VK_LCONTROL:
r.ctrlKey = true
case VK_MENU: //alt
r.altKey = true
case VK_LEFT:
target = CharBackward
case VK_RIGHT:
target = CharForward
case VK_UP:
target = CharPrev
case VK_DOWN:
target = CharNext
}
if target != 0 {
return r.write(buf, target)
}
goto next
}
char := rune(ker.unicodeChar)
if r.ctrlKey {
switch char {
case 'A':
char = CharLineStart
case 'E':
char = CharLineEnd
case 'R':
char = CharBckSearch
case 'S':
char = CharFwdSearch
}
} else if r.altKey {
switch char {
case VK_BACK:
char = CharBackspace
}
return r.writeEsc(buf, char)
}
return r.write(buf, char)
}
func (r *RawReader) writeEsc(b []byte, char rune) (int, error) {
b[0] = '\033'
n := copy(b[1:], []byte(string(char)))
return n + 1, nil
}
func (r *RawReader) write(b []byte, char rune) (int, error) {
n := copy(b, []byte(string(char)))
return n, nil
}
func (r *RawReader) Close() error {
return nil
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/readline.go
================================================
// Readline is a pure go implementation for GNU-Readline kind library.
//
// example:
// rl, err := readline.New("> ")
// if err != nil {
// panic(err)
// }
// defer rl.Close()
//
// for {
// line, err := rl.Readline()
// if err != nil { // io.EOF
// break
// }
// println(line)
// }
//
package readline
import "io"
type Instance struct {
Config *Config
Terminal *Terminal
Operation *Operation
}
type Config struct {
// prompt supports ANSI escape sequence, so we can color some characters even in windows
Prompt string
// readline will persist historys to file where HistoryFile specified
HistoryFile string
// specify the max length of historys, it's 500 by default, set it to -1 to disable history
HistoryLimit int
DisableAutoSaveHistory bool
// enable case-insensitive history searching
HistorySearchFold bool
// AutoCompleter will called once user press TAB
AutoComplete AutoCompleter
// Any key press will pass to Listener
// NOTE: Listener will be triggered by (nil, 0, 0) immediately
Listener Listener
// If VimMode is true, readline will in vim.insert mode by default
VimMode bool
InterruptPrompt string
EOFPrompt string
FuncGetWidth func() int
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
EnableMask bool
MaskRune rune
// erase the editing line after user submited it
// it use in IM usually.
UniqueEditLine bool
// force use interactive even stdout is not a tty
FuncIsTerminal func() bool
FuncMakeRaw func() error
FuncExitRaw func() error
FuncOnWidthChanged func(func())
ForceUseInteractive bool
// private fields
inited bool
opHistory *opHistory
}
func (c *Config) useInteractive() bool {
if c.ForceUseInteractive {
return true
}
return c.FuncIsTerminal()
}
func (c *Config) Init() error {
if c.inited {
return nil
}
c.inited = true
if c.Stdin == nil {
c.Stdin = NewCancelableStdin(Stdin)
}
if c.Stdout == nil {
c.Stdout = Stdout
}
if c.Stderr == nil {
c.Stderr = Stderr
}
if c.HistoryLimit == 0 {
c.HistoryLimit = 500
}
if c.InterruptPrompt == "" {
c.InterruptPrompt = "^C"
} else if c.InterruptPrompt == "\n" {
c.InterruptPrompt = ""
}
if c.EOFPrompt == "" {
c.EOFPrompt = "^D"
} else if c.EOFPrompt == "\n" {
c.EOFPrompt = ""
}
if c.AutoComplete == nil {
c.AutoComplete = &TabCompleter{}
}
if c.FuncGetWidth == nil {
c.FuncGetWidth = GetScreenWidth
}
if c.FuncIsTerminal == nil {
c.FuncIsTerminal = DefaultIsTerminal
}
rm := new(RawMode)
if c.FuncMakeRaw == nil {
c.FuncMakeRaw = rm.Enter
}
if c.FuncExitRaw == nil {
c.FuncExitRaw = rm.Exit
}
if c.FuncOnWidthChanged == nil {
c.FuncOnWidthChanged = DefaultOnWidthChanged
}
return nil
}
func (c Config) Clone() *Config {
c.opHistory = nil
return &c
}
func (c *Config) SetListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) {
c.Listener = FuncListener(f)
}
func NewEx(cfg *Config) (*Instance, error) {
t, err := NewTerminal(cfg)
if err != nil {
return nil, err
}
rl := t.Readline()
return &Instance{
Config: cfg,
Terminal: t,
Operation: rl,
}, nil
}
func New(prompt string) (*Instance, error) {
return NewEx(&Config{Prompt: prompt})
}
func (i *Instance) ResetHistory() {
i.Operation.ResetHistory()
}
func (i *Instance) SetPrompt(s string) {
i.Operation.SetPrompt(s)
}
func (i *Instance) SetMaskRune(r rune) {
i.Operation.SetMaskRune(r)
}
// change history persistence in runtime
func (i *Instance) SetHistoryPath(p string) {
i.Operation.SetHistoryPath(p)
}
// readline will refresh automatic when write through Stdout()
func (i *Instance) Stdout() io.Writer {
return i.Operation.Stdout()
}
// readline will refresh automatic when write through Stdout()
func (i *Instance) Stderr() io.Writer {
return i.Operation.Stderr()
}
// switch VimMode in runtime
func (i *Instance) SetVimMode(on bool) {
i.Operation.SetVimMode(on)
}
func (i *Instance) IsVimMode() bool {
return i.Operation.IsEnableVimMode()
}
func (i *Instance) GenPasswordConfig() *Config {
return i.Operation.GenPasswordConfig()
}
// we can generate a config by `i.GenPasswordConfig()`
func (i *Instance) ReadPasswordWithConfig(cfg *Config) ([]byte, error) {
return i.Operation.PasswordWithConfig(cfg)
}
func (i *Instance) ReadPasswordEx(prompt string, l Listener) ([]byte, error) {
return i.Operation.PasswordEx(prompt, l)
}
func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
return i.Operation.Password(prompt)
}
type Result struct {
Line string
Error error
}
func (l *Result) CanContinue() bool {
return len(l.Line) != 0 && l.Error == ErrInterrupt
}
func (l *Result) CanBreak() bool {
return !l.CanContinue() && l.Error != nil
}
func (i *Instance) Line() *Result {
ret, err := i.Readline()
return &Result{ret, err}
}
// err is one of (nil, io.EOF, readline.ErrInterrupt)
func (i *Instance) Readline() (string, error) {
return i.Operation.String()
}
func (i *Instance) SaveHistory(content string) error {
return i.Operation.SaveHistory(content)
}
// same as readline
func (i *Instance) ReadSlice() ([]byte, error) {
return i.Operation.Slice()
}
// we must make sure that call Close() before process exit.
func (i *Instance) Close() error {
if err := i.Terminal.Close(); err != nil {
return err
}
i.Operation.Close()
return nil
}
func (i *Instance) Clean() {
i.Operation.Clean()
}
func (i *Instance) Write(b []byte) (int, error) {
return i.Stdout().Write(b)
}
func (i *Instance) SetConfig(cfg *Config) *Config {
if i.Config == cfg {
return cfg
}
old := i.Config
i.Config = cfg
i.Operation.SetConfig(cfg)
i.Terminal.SetConfig(cfg)
return old
}
func (i *Instance) Refresh() {
i.Operation.Refresh()
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/readline_test.go
================================================
package readline
import (
"testing"
"time"
)
func TestRace(t *testing.T) {
rl, err := NewEx(&Config{})
if err != nil {
t.Fatal(err)
return
}
go func() {
for range time.Tick(time.Millisecond) {
rl.SetPrompt("hello")
}
}()
go func() {
time.Sleep(100 * time.Millisecond)
rl.Close()
}()
rl.Readline()
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/remote.go
================================================
package readline
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
"os"
"sync"
"sync/atomic"
)
type MsgType int16
const (
T_DATA = MsgType(iota)
T_WIDTH
T_WIDTH_REPORT
T_ISTTY_REPORT
T_RAW
T_ERAW // exit raw
T_EOF
)
type RemoteSvr struct {
eof int32
closed int32
width int32
reciveChan chan struct{}
writeChan chan *writeCtx
conn net.Conn
isTerminal bool
funcWidthChan func()
stopChan chan struct{}
dataBufM sync.Mutex
dataBuf bytes.Buffer
}
type writeReply struct {
n int
err error
}
type writeCtx struct {
msg *Message
reply chan *writeReply
}
func newWriteCtx(msg *Message) *writeCtx {
return &writeCtx{
msg: msg,
reply: make(chan *writeReply),
}
}
func NewRemoteSvr(conn net.Conn) (*RemoteSvr, error) {
rs := &RemoteSvr{
width: -1,
conn: conn,
writeChan: make(chan *writeCtx),
reciveChan: make(chan struct{}),
stopChan: make(chan struct{}),
}
buf := bufio.NewReader(rs.conn)
if err := rs.init(buf); err != nil {
return nil, err
}
go rs.readLoop(buf)
go rs.writeLoop()
return rs, nil
}
func (r *RemoteSvr) init(buf *bufio.Reader) error {
m, err := ReadMessage(buf)
if err != nil {
return err
}
// receive isTerminal
if m.Type != T_ISTTY_REPORT {
return fmt.Errorf("unexpected init message")
}
r.GotIsTerminal(m.Data)
// receive width
m, err = ReadMessage(buf)
if err != nil {
return err
}
if m.Type != T_WIDTH_REPORT {
return fmt.Errorf("unexpected init message")
}
r.GotReportWidth(m.Data)
return nil
}
func (r *RemoteSvr) HandleConfig(cfg *Config) {
cfg.Stderr = r
cfg.Stdout = r
cfg.Stdin = r
cfg.FuncExitRaw = r.ExitRawMode
cfg.FuncIsTerminal = r.IsTerminal
cfg.FuncMakeRaw = r.EnterRawMode
cfg.FuncExitRaw = r.ExitRawMode
cfg.FuncGetWidth = r.GetWidth
cfg.FuncOnWidthChanged = func(f func()) {
r.funcWidthChan = f
}
}
func (r *RemoteSvr) IsTerminal() bool {
return r.isTerminal
}
func (r *RemoteSvr) checkEOF() error {
if atomic.LoadInt32(&r.eof) == 1 {
return io.EOF
}
return nil
}
func (r *RemoteSvr) Read(b []byte) (int, error) {
r.dataBufM.Lock()
n, err := r.dataBuf.Read(b)
r.dataBufM.Unlock()
if n == 0 {
if err := r.checkEOF(); err != nil {
return 0, err
}
}
if n == 0 && err == io.EOF {
<-r.reciveChan
r.dataBufM.Lock()
n, err = r.dataBuf.Read(b)
r.dataBufM.Unlock()
}
if n == 0 {
if err := r.checkEOF(); err != nil {
return 0, err
}
}
return n, err
}
func (r *RemoteSvr) writeMsg(m *Message) error {
ctx := newWriteCtx(m)
r.writeChan <- ctx
reply := <-ctx.reply
return reply.err
}
func (r *RemoteSvr) Write(b []byte) (int, error) {
ctx := newWriteCtx(NewMessage(T_DATA, b))
r.writeChan <- ctx
reply := <-ctx.reply
return reply.n, reply.err
}
func (r *RemoteSvr) EnterRawMode() error {
return r.writeMsg(NewMessage(T_RAW, nil))
}
func (r *RemoteSvr) ExitRawMode() error {
return r.writeMsg(NewMessage(T_ERAW, nil))
}
func (r *RemoteSvr) writeLoop() {
defer r.Close()
loop:
for {
select {
case ctx, ok := <-r.writeChan:
if !ok {
break
}
n, err := ctx.msg.WriteTo(r.conn)
ctx.reply <- &writeReply{n, err}
case <-r.stopChan:
break loop
}
}
}
func (r *RemoteSvr) Close() {
if atomic.CompareAndSwapInt32(&r.closed, 0, 1) {
close(r.stopChan)
r.conn.Close()
}
}
func (r *RemoteSvr) readLoop(buf *bufio.Reader) {
defer r.Close()
for {
m, err := ReadMessage(buf)
if err != nil {
break
}
switch m.Type {
case T_EOF:
atomic.StoreInt32(&r.eof, 1)
select {
case r.reciveChan <- struct{}{}:
default:
}
case T_DATA:
r.dataBufM.Lock()
r.dataBuf.Write(m.Data)
r.dataBufM.Unlock()
select {
case r.reciveChan <- struct{}{}:
default:
}
case T_WIDTH_REPORT:
r.GotReportWidth(m.Data)
case T_ISTTY_REPORT:
r.GotIsTerminal(m.Data)
}
}
}
func (r *RemoteSvr) GotIsTerminal(data []byte) {
if binary.BigEndian.Uint16(data) == 0 {
r.isTerminal = false
} else {
r.isTerminal = true
}
}
func (r *RemoteSvr) GotReportWidth(data []byte) {
atomic.StoreInt32(&r.width, int32(binary.BigEndian.Uint16(data)))
if r.funcWidthChan != nil {
r.funcWidthChan()
}
}
func (r *RemoteSvr) GetWidth() int {
return int(atomic.LoadInt32(&r.width))
}
// -----------------------------------------------------------------------------
type Message struct {
Type MsgType
Data []byte
}
func ReadMessage(r io.Reader) (*Message, error) {
m := new(Message)
var length int32
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &m.Type); err != nil {
return nil, err
}
m.Data = make([]byte, int(length)-2)
if _, err := io.ReadFull(r, m.Data); err != nil {
return nil, err
}
return m, nil
}
func NewMessage(t MsgType, data []byte) *Message {
return &Message{t, data}
}
func (m *Message) WriteTo(w io.Writer) (int, error) {
buf := bytes.NewBuffer(make([]byte, 0, len(m.Data)+2+4))
binary.Write(buf, binary.BigEndian, int32(len(m.Data)+2))
binary.Write(buf, binary.BigEndian, m.Type)
buf.Write(m.Data)
n, err := buf.WriteTo(w)
return int(n), err
}
// -----------------------------------------------------------------------------
type RemoteCli struct {
conn net.Conn
raw RawMode
receiveChan chan struct{}
inited int32
isTerminal *bool
data bytes.Buffer
dataM sync.Mutex
}
func NewRemoteCli(conn net.Conn) (*RemoteCli, error) {
r := &RemoteCli{
conn: conn,
receiveChan: make(chan struct{}),
}
return r, nil
}
func (r *RemoteCli) MarkIsTerminal(is bool) {
r.isTerminal = &is
}
func (r *RemoteCli) init() error {
if !atomic.CompareAndSwapInt32(&r.inited, 0, 1) {
return nil
}
if err := r.reportIsTerminal(); err != nil {
return err
}
if err := r.reportWidth(); err != nil {
return err
}
// register sig for width changed
DefaultOnWidthChanged(func() {
r.reportWidth()
})
return nil
}
func (r *RemoteCli) writeMsg(m *Message) error {
r.dataM.Lock()
_, err := m.WriteTo(r.conn)
r.dataM.Unlock()
return err
}
func (r *RemoteCli) Write(b []byte) (int, error) {
m := NewMessage(T_DATA, b)
r.dataM.Lock()
_, err := m.WriteTo(r.conn)
r.dataM.Unlock()
return len(b), err
}
func (r *RemoteCli) reportWidth() error {
screenWidth := GetScreenWidth()
data := make([]byte, 2)
binary.BigEndian.PutUint16(data, uint16(screenWidth))
msg := NewMessage(T_WIDTH_REPORT, data)
if err := r.writeMsg(msg); err != nil {
return err
}
return nil
}
func (r *RemoteCli) reportIsTerminal() error {
var isTerminal bool
if r.isTerminal != nil {
isTerminal = *r.isTerminal
} else {
isTerminal = DefaultIsTerminal()
}
data := make([]byte, 2)
if isTerminal {
binary.BigEndian.PutUint16(data, 1)
} else {
binary.BigEndian.PutUint16(data, 0)
}
msg := NewMessage(T_ISTTY_REPORT, data)
if err := r.writeMsg(msg); err != nil {
return err
}
return nil
}
func (r *RemoteCli) readLoop() {
buf := bufio.NewReader(r.conn)
for {
msg, err := ReadMessage(buf)
if err != nil {
break
}
switch msg.Type {
case T_ERAW:
r.raw.Exit()
case T_RAW:
r.raw.Enter()
case T_DATA:
os.Stdout.Write(msg.Data)
}
}
}
func (r *RemoteCli) ServeBy(source io.Reader) error {
if err := r.init(); err != nil {
return err
}
go func() {
defer r.Close()
for {
n, _ := io.Copy(r, source)
if n == 0 {
break
}
}
}()
defer r.raw.Exit()
r.readLoop()
return nil
}
func (r *RemoteCli) Close() {
r.writeMsg(NewMessage(T_EOF, nil))
}
func (r *RemoteCli) Serve() error {
return r.ServeBy(os.Stdin)
}
func ListenRemote(n, addr string, cfg *Config, h func(*Instance), onListen ...func(net.Listener) error) error {
ln, err := net.Listen(n, addr)
if err != nil {
return err
}
if len(onListen) > 0 {
if err := onListen[0](ln); err != nil {
return err
}
}
for {
conn, err := ln.Accept()
if err != nil {
break
}
go func() {
defer conn.Close()
rl, err := HandleConn(*cfg, conn)
if err != nil {
return
}
h(rl)
}()
}
return nil
}
func HandleConn(cfg Config, conn net.Conn) (*Instance, error) {
r, err := NewRemoteSvr(conn)
if err != nil {
return nil, err
}
r.HandleConfig(&cfg)
rl, err := NewEx(&cfg)
if err != nil {
return nil, err
}
return rl, nil
}
func DialRemote(n, addr string) error {
conn, err := net.Dial(n, addr)
if err != nil {
return err
}
defer conn.Close()
cli, err := NewRemoteCli(conn)
if err != nil {
return err
}
return cli.Serve()
}
================================================
FILE: cmd/nash/vendor/github.com/chzyer/readline/runebuf.go
================================================
package readline
import (
"bufio"
"bytes"
"io"
"strings"
"sync"
)
type runeBufferBck struct {
buf []rune
idx int
}
type RuneBuffer struct {
buf []rune
idx int
prompt []rune
w io.Writer
hadClean bool
interactive bool
cfg *Config
width int
bck *runeBufferBck
offset string
sync.Mutex
}
func (r *RuneBuffer) OnWidthChange(newWidth int) {
r.Lock()
r.width = newWidth
r.Unlock()
}
func (r *RuneBuffer) Backup() {
r.Lock()
r.bck = &runeBufferBck{r.buf, r.idx}
r.Unlock()
}
func (r *RuneBuffer) Restore() {
r.Refresh(func() {
if r.bck == nil {
return
}
r.buf = r.bck.buf
r.idx = r.bck.idx
})
}
func NewRuneBuffer(w io.Writer, prompt string, cfg *Config, width int) *RuneBuffer {
rb := &RuneBuffer{
w: w,
interactive: cfg.useInteractive(),
cfg: cfg,
width: width,
}
rb.SetPrompt(prompt)
return rb
}
func (r *RuneBuffer) SetConfig(cfg *Config) {
r.Lock()
r.cfg = cfg
r.interactive = cfg.useInteractive()
r.Unlock()
}
func (r *RuneBuffer) SetMask(m rune) {
r.Lock()
r.cfg.MaskRune = m
r.Unlock()
}
func (r *RuneBuffer) CurrentWidth(x int) int {
r.Lock()
defer r.Unlock()
return runes.WidthAll(r.buf[:x])
}
func (r *RuneBuffer) PromptLen() int {
r.Lock()
width := r.promptLen()
r.Unlock()
return width
}
func (r *RuneBuffer) promptLen() int {
return runes.WidthAll(runes.ColorFilter(r.prompt))
}
func (r *RuneBuffer) RuneSlice(i int) []rune {
r.Lock()
defer r.Unlock()
if i > 0 {
rs := make([]rune, i)
copy(rs, r.buf[r.idx:r.idx+i])
return rs
}
rs := make([]rune, -i)
copy(rs, r.buf[r.idx+i:r.idx])
return rs
}
func (r *RuneBuffer) Runes() []rune {
r.Lock()
newr := make([]rune, len(r.buf))
copy(newr, r.buf)
r.Unlock()
return newr
}
func (r *RuneBuffer) Pos() int {
r.Lock()
defer r.Unlock()
return r.idx
}
func (r *RuneBuffer) Len() int {
r.Lock()
defer r.Unlock()
return len(r.buf)
}
func (r *RuneBuffer) MoveToLineStart() {
r.Refresh(func() {
if r.idx == 0 {
return
}
r.idx = 0
})
}
func (r *RuneBuffer) MoveBackward() {
r.Refresh(func() {
if r.idx == 0 {
return
}
r.idx--
})
}
func (r *RuneBuffer) WriteString(s string) {
r.WriteRunes([]rune(s))
}
func (r *RuneBuffer) WriteRune(s rune) {
r.WriteRunes([]rune{s})
}
func (r *RuneBuffer) WriteRunes(s []rune) {
r.Refresh(func() {
tail := append(s, r.buf[r.idx:]...)
r.buf = append(r.buf[:r.idx], tail...)
r.idx += len(s)
})
}
func (r *RuneBuffer) MoveForward() {
r.Refresh(func() {
if r.idx == len(r.buf) {
return
}
r.idx++
})
}
func (r *RuneBuffer) IsCursorInEnd() bool {
r.Lock()
defer r.Unlock()
return r.idx == len(r.buf)
}
func (r *RuneBuffer) Replace(ch rune) {
r.Refresh(func() {
r.buf[r.idx] = ch
})
}
func (r *RuneBuffer) Erase() {
r.Refresh(func() {
r.idx = 0
r.buf = r.buf[:0]
})
}
func (r *RuneBuffer) Delete() (success bool) {
r.Refresh(func() {
if r.idx == len(r.buf) {
return
}
r.buf = append(r.buf[:r.idx], r.buf[r.idx+1:]...)
success = true
})
return
}
func (r *RuneBuffer) DeleteWord() {
if r.idx == len(r.buf) {
return
}
init := r.idx
for init < len(r.buf) && IsWordBreak(r.buf[init]) {
init++
}
for i := init + 1; i < len(r.buf); i++ {
if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
r.Refresh(func() {
r.buf = append(r.buf[:r.idx], r.buf[i-1:]...)
})
return
}
}
r.Kill()
}
func (r *RuneBuffer) MoveToPrevWord() (success bool) {
r.Refresh(func() {
if r.idx == 0 {
return
}
for i := r.idx - 1; i > 0; i-- {
if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
r.idx = i
success = true
return
}
}
r.idx = 0
success = true
})
return
}
func (r *RuneBuffer) KillFront() {
r.Refresh(func() {
if r.idx == 0 {
return
}
length := len(r.buf) - r.idx
copy(r.buf[:length], r.buf[r.idx:])
r.idx = 0
r.buf = r.buf[:length]
})
}
func (r *RuneBuffer) Kill() {
r.Refresh(func() {
r.buf = r.buf[:r.idx]
})
}
func (r *RuneBuffer) Transpose() {
r.Refresh(func() {
if len(r.buf) == 1 {
r.idx++
}
if len(r.buf) < 2 {
return
}
if r.idx == 0 {
r.idx = 1
} else if r.idx >= len(r.buf) {
r.idx = len(r.buf) - 1
}
r.buf[r.idx], r.buf[r.idx-1] = r.buf[r.idx-1], r.buf[r.idx]
r.idx++
})
}
func (r *RuneBuffer) MoveToNextWord() {
r.Refresh(func() {
for i := r.idx + 1; i < len(r.buf); i++ {
if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
r.idx = i
return
}
}
r.idx = len(r.buf)
})
}
func (r *RuneBuffer) MoveToEndWord() {
r.Refresh(func() {
// already at the end, so do nothing
if r.idx == len(r.buf) {
return
}
// if we are at the end of a word already, go to next
if !IsWordBreak(r.buf[r.idx]) && IsWordBreak(r.buf[r.idx+1]) {
r.idx++
}
// keep going until at the end of a word
for i := r.idx + 1; i < len(r.buf); i++ {
if IsWordBreak(r.buf[i]) && !IsWordBreak(r.buf[i-1]) {
r.idx = i - 1
return
}
}
r.idx = len(r.buf)
})
}
func (r *RuneBuffer) BackEscapeWord() {
r.Refresh(func() {
if r.idx == 0 {
return
}
for i := r.idx - 1; i > 0; i-- {
if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
r.buf = append(r.buf[:i], r.buf[r.idx:]...)
r.idx = i
return
}
}
r.buf = r.buf[:0]
r.idx = 0
})
}
func (r *RuneBuffer) Backspace() {
r.Refresh(func() {
if r.idx == 0 {
return
}
r.idx--
r.buf = append(r.buf[:r.idx], r.buf[r.idx+1:]...)
})
}
func (r *RuneBuffer) MoveToLineEnd() {
r.Refresh(func() {
if r.idx == len(r.buf) {
return
}
r.idx = len(r.buf)
})
}
func (r *RuneBuffer) LineCount(width int) int {
if width == -1 {
width = r.width
}
return LineCount(width,
runes.WidthAll(r.buf)+r.PromptLen())
}
func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
r.Refresh(func() {
if reverse {
for i := r.idx - 1; i >= 0; i-- {
if r.buf[i] == ch {
r.idx = i
if prevChar {
r.idx++
}
success = true
return
}
}
return
}
for i := r.idx + 1; i < len(r.buf); i++ {
if r.buf[i] == ch {
r.idx = i
if prevChar {
r.idx--
}
success = true
return
}
}
})
return
}
func (r *RuneBuffer) isInLineEdge() bool {
if isWindows {
return false
}
sp := r.getSplitByLine(r.buf)
return len(sp[len(sp)-1]) == 0
}
func (r *RuneBuffer) getSplitByLine(rs []rune) []string {
return SplitByLine(r.promptLen(), r.width, rs)
}
func (r *RuneBuffer) IdxLine(width int) int {
r.Lock()
defer r.Unlock()
return r.idxLine(width)
}
func (r *RuneBuffer) idxLine(width int) int {
if width == 0 {
return 0
}
sp := r.getSplitByLine(r.buf[:r.idx])
return len(sp) - 1
}
func (r *RuneBuffer) CursorLineCount() int {
return r.LineCount(r.width) - r.IdxLine(r.width)
}
func (r *RuneBuffer) Refresh(f func()) {
r.Lock()
defer r.Unlock()
if !r.interactive {
if f != nil {
f()
}
return
}
r.clean()
if f != nil {
f()
}
r.print()
}
func (r *RuneBuffer) SetOffset(offset string) {
r.Lock()
r.offset = offset
r.Unlock()
}
func (r *RuneBuffer) print() {
r.w.Write(r.output())
r.hadClean = false
}
func (r *RuneBuffer) output() []byte {
buf := bytes.NewBuffer(nil)
buf.WriteString(string(r.prompt))
if r.cfg.EnableMask && len(r.buf) > 0 {
buf.Write([]byte(strings.Repeat(string(r.cfg.MaskRune), len(r.buf)-1)))
if r.buf[len(r.buf)-1] == '\n' {
buf.Write([]byte{'\n'})
} else {
buf.Write([]byte(string(r.cfg.MaskRune)))
}
if len(r.buf) > r.idx {
buf.Write(runes.Backspace(r.buf[r.idx:]))
}
} else {
for idx := range r.buf {
if r.buf[idx] == '\t' {
buf.WriteString(strings.Repeat(" ", TabWidth))
} else {
buf.WriteRune(r.buf[idx])
}
}
if r.isInLineEdge() {
buf.Write([]byte(" \b"))
}
}
if len(r.buf) > r.idx {
buf.Write(runes.Backspace(r.buf[r.idx:]))
}
return buf.Bytes()
}
func (r *RuneBuffer) Reset() []rune {
ret := runes.Copy(r.buf)
r.buf = r.buf[:0]
r.idx = 0
return ret
}
func (r *RuneBuffer) calWidth(m int) int {
if m > 0 {
return runes.WidthAll(r.buf[r.idx : r.idx+m])
}
return runes.WidthAll(r.buf[r.idx+m : r.idx])
}
func (r *RuneBuffer) SetStyle(start, end int, style string) {
if end < start {
panic("end < start")
}
// goto start
move := start - r.idx
if move > 0 {
r.w.Write([]byte(string(r.buf[r.idx : r.idx+move])))
} else {
r.w.Write(bytes.Repeat([]byte("\b"), r.calWidth(move)))
}
r.w.Write([]byte("\033[" + style + "m"))
r.w.Write([]byte(string(r.buf[start:end])))
r.w.Write([]byte("\033[0m"))
// TODO: move back
}
func (r *RuneBuffer) SetWithIdx(idx int, buf []rune) {
r.Refre
gitextract_5jvfgdox/
├── .gitignore
├── .travis.yml
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── _disabled_appveyor.yml
├── ast/
│ ├── doc_test.go
│ ├── node.go
│ ├── node_args.go
│ ├── node_fmt.go
│ ├── node_fmt_test.go
│ ├── nodetype_string.go
│ ├── tree.go
│ └── tree_test.go
├── cmd/
│ ├── nash/
│ │ ├── cli.go
│ │ ├── completer.go
│ │ ├── env.go
│ │ ├── env_test.go
│ │ ├── example.sh
│ │ ├── install.go
│ │ ├── install_test.go
│ │ ├── main.go
│ │ ├── rpc.go
│ │ ├── vendor/
│ │ │ └── github.com/
│ │ │ └── chzyer/
│ │ │ └── readline/
│ │ │ ├── .travis.yml
│ │ │ ├── CHANGELOG.md
│ │ │ ├── LICENSE
│ │ │ ├── README.md
│ │ │ ├── ansi_windows.go
│ │ │ ├── complete.go
│ │ │ ├── complete_helper.go
│ │ │ ├── complete_segment.go
│ │ │ ├── complete_segment_test.go
│ │ │ ├── doc/
│ │ │ │ └── shortcut.md
│ │ │ ├── example/
│ │ │ │ ├── readline-demo/
│ │ │ │ │ └── readline-demo.go
│ │ │ │ ├── readline-im/
│ │ │ │ │ ├── README.md
│ │ │ │ │ └── readline-im.go
│ │ │ │ ├── readline-multiline/
│ │ │ │ │ └── readline-multiline.go
│ │ │ │ ├── readline-pass-strength/
│ │ │ │ │ └── readline-pass-strength.go
│ │ │ │ └── readline-remote/
│ │ │ │ ├── readline-remote-client/
│ │ │ │ │ └── client.go
│ │ │ │ └── readline-remote-server/
│ │ │ │ └── server.go
│ │ │ ├── history.go
│ │ │ ├── operation.go
│ │ │ ├── password.go
│ │ │ ├── rawreader_windows.go
│ │ │ ├── readline.go
│ │ │ ├── readline_test.go
│ │ │ ├── remote.go
│ │ │ ├── runebuf.go
│ │ │ ├── runes/
│ │ │ │ ├── runes.go
│ │ │ │ └── runes_test.go
│ │ │ ├── runes.go
│ │ │ ├── runes_test.go
│ │ │ ├── std.go
│ │ │ ├── std_windows.go
│ │ │ ├── term.go
│ │ │ ├── term_bsd.go
│ │ │ ├── term_linux.go
│ │ │ ├── term_windows.go
│ │ │ ├── terminal.go
│ │ │ ├── utils.go
│ │ │ ├── utils_test.go
│ │ │ ├── utils_unix.go
│ │ │ ├── utils_windows.go
│ │ │ ├── vim.go
│ │ │ └── windows_api.go
│ │ └── vendor.sh
│ └── nashfmt/
│ └── main.go
├── docs/
│ ├── interactive.md
│ ├── reference.md
│ └── stdlib/
│ └── fmt.md
├── errors/
│ └── error.go
├── examples/
│ ├── append.sh
│ ├── args.sh
│ ├── init
│ └── len.sh
├── examples_test.go
├── fuzz.go
├── hack/
│ ├── check.sh
│ ├── install/
│ │ └── unix.sh
│ └── releaser.sh
├── internal/
│ ├── sh/
│ │ ├── builtin/
│ │ │ ├── append.go
│ │ │ ├── append_test.go
│ │ │ ├── chdir.go
│ │ │ ├── doc.go
│ │ │ ├── exec_test.go
│ │ │ ├── exit.go
│ │ │ ├── exit_test.go
│ │ │ ├── format.go
│ │ │ ├── format_test.go
│ │ │ ├── glob.go
│ │ │ ├── glob_test.go
│ │ │ ├── len.go
│ │ │ ├── len_test.go
│ │ │ ├── loader.go
│ │ │ ├── print.go
│ │ │ ├── print_test.go
│ │ │ ├── split.go
│ │ │ ├── split_test.go
│ │ │ └── testdata/
│ │ │ ├── exit.sh
│ │ │ ├── split.sh
│ │ │ └── splitfunc.sh
│ │ ├── builtin.go
│ │ ├── cmd.go
│ │ ├── cmd_test.go
│ │ ├── fncall.go
│ │ ├── fndef.go
│ │ ├── functions_test.go
│ │ ├── internal/
│ │ │ └── fixture/
│ │ │ └── fixture.go
│ │ ├── ioutils_test.go
│ │ ├── log.go
│ │ ├── rfork.go
│ │ ├── rfork_linux.go
│ │ ├── rfork_linux_test.go
│ │ ├── rfork_plan9.go
│ │ ├── shell.go
│ │ ├── shell_import_test.go
│ │ ├── shell_linux_test.go
│ │ ├── shell_regression_test.go
│ │ ├── shell_test.go
│ │ ├── shell_var_test.go
│ │ ├── util.go
│ │ └── util_test.go
│ └── testing/
│ └── fixture/
│ └── io.go
├── nash.go
├── nash_test.go
├── parser/
│ ├── parse.go
│ ├── parse_fmt_test.go
│ ├── parse_regression_test.go
│ └── parse_test.go
├── proposal/
│ ├── 1-scope-management.md
│ └── 2-concurrency.md
├── scanner/
│ ├── examples_test.go
│ ├── lex.go
│ ├── lex_regression_test.go
│ └── lex_test.go
├── sh/
│ ├── obj.go
│ ├── objtype_string.go
│ └── shell.go
├── spec.ebnf
├── spec_test.go
├── stdbin/
│ ├── mkdir/
│ │ ├── main.go
│ │ └── mkdir_test.go
│ ├── pwd/
│ │ └── main.go
│ ├── strings/
│ │ ├── main.go
│ │ ├── strings.go
│ │ └── strings_test.go
│ └── write/
│ ├── common_test.sh
│ ├── fd.go
│ ├── fd_windows.go
│ ├── main.go
│ ├── write.go
│ ├── write_linux_test.sh
│ └── write_test.sh
├── stdlib/
│ ├── io.sh
│ ├── io_example.sh
│ ├── io_test.sh
│ ├── map.sh
│ └── map_test.sh
├── testfiles/
│ ├── ex1.sh
│ ├── fibonacci.sh
│ └── sieve.sh
├── tests/
│ ├── cfg.go
│ ├── doc.go
│ ├── internal/
│ │ ├── assert/
│ │ │ ├── doc.go
│ │ │ ├── equal.go
│ │ │ └── error.go
│ │ ├── sh/
│ │ │ └── shell.go
│ │ └── tester/
│ │ └── tester.go
│ ├── listindex_test.go
│ └── stringindex_test.go
├── token/
│ └── token.go
└── vendor/
└── golang.org/
└── x/
└── exp/
└── ebnf/
├── ebnf.go
└── parser.go
SYMBOL INDEX (1605 symbols across 127 files)
FILE: ast/doc_test.go
function Example_AssignmentNode (line 10) | func Example_AssignmentNode() {
function Example_AssignmentNode_Single (line 25) | func Example_AssignmentNode_Single() {
FILE: ast/node.go
constant RedirMapNoValue (line 12) | RedirMapNoValue = -1
constant RedirMapSupress (line 14) | RedirMapSupress = -2
constant RforkFlags (line 16) | RforkFlags = "umnips"
type Node (line 21) | type Node interface
type assignable (line 36) | type assignable interface
type egalitarian (line 43) | type egalitarian struct
method equal (line 442) | func (e egalitarian) equal(node, other Node) bool {
type Expr (line 46) | type Expr
type NodeType (line 49) | type NodeType
method Type (line 428) | func (t NodeType) Type() NodeType {
method IsExpr (line 433) | func (t NodeType) IsExpr() bool {
method IsExecutable (line 438) | func (t NodeType) IsExecutable() bool {
type BlockNode (line 52) | type BlockNode struct
method Push (line 467) | func (l *BlockNode) Push(n Node) {
method IsEqual (line 472) | func (l *BlockNode) IsEqual(other Node) bool {
type ImportNode (line 61) | type ImportNode struct
method IsEqual (line 510) | func (n *ImportNode) IsEqual(other Node) bool {
type SetenvNode (line 70) | type SetenvNode struct
method Assignment (line 548) | func (n *SetenvNode) Assignment() Node { return n.assign }
method IsEqual (line 551) | func (n *SetenvNode) IsEqual(other Node) bool {
type NameNode (line 79) | type NameNode struct
method IsEqual (line 581) | func (n *NameNode) IsEqual(other Node) bool {
type AssignNode (line 89) | type AssignNode struct
method names (line 631) | func (n *AssignNode) names() []*NameNode { return n.Names }
method getEqSpace (line 632) | func (n *AssignNode) getEqSpace() int { return n.eqSpace }
method setEqSpace (line 633) | func (n *AssignNode) setEqSpace(value int) { n.eqSpace = value }
method IsEqual (line 636) | func (n *AssignNode) IsEqual(other Node) bool {
type ExecAssignNode (line 100) | type ExecAssignNode struct
method names (line 695) | func (n *ExecAssignNode) names() []*NameNode { return n.Names }
method getEqSpace (line 696) | func (n *ExecAssignNode) getEqSpace() int { return n.eqSpace }
method setEqSpace (line 697) | func (n *ExecAssignNode) setEqSpace(value int) { n.eqSpace = value }
method Command (line 700) | func (n *ExecAssignNode) Command() Node {
method SetCommand (line 705) | func (n *ExecAssignNode) SetCommand(c Node) {
method IsEqual (line 709) | func (n *ExecAssignNode) IsEqual(other Node) bool {
type CommandNode (line 111) | type CommandNode struct
method IsMulti (line 754) | func (n *CommandNode) IsMulti() bool { return n.multi }
method SetMulti (line 755) | func (n *CommandNode) SetMulti(b bool) { n.multi = b }
method AddArg (line 758) | func (n *CommandNode) AddArg(a Expr) {
method SetArgs (line 763) | func (n *CommandNode) SetArgs(args []Expr) {
method Args (line 768) | func (n *CommandNode) Args() []Expr { return n.args }
method AddRedirect (line 771) | func (n *CommandNode) AddRedirect(redir *RedirectNode) {
method Redirects (line 776) | func (n *CommandNode) Redirects() []*RedirectNode { return n.redirs }
method Name (line 779) | func (n *CommandNode) Name() string { return n.name }
method IsEqual (line 782) | func (n *CommandNode) IsEqual(other Node) bool {
type PipeNode (line 124) | type PipeNode struct
method IsMulti (line 843) | func (n *PipeNode) IsMulti() bool { return n.multi }
method SetMulti (line 844) | func (n *PipeNode) SetMulti(b bool) { n.multi = b }
method AddCmd (line 847) | func (n *PipeNode) AddCmd(c *CommandNode) {
method Commands (line 852) | func (n *PipeNode) Commands() []*CommandNode {
method IsEqual (line 857) | func (n *PipeNode) IsEqual(other Node) bool {
type StringExpr (line 134) | type StringExpr struct
type IntExpr (line 144) | type IntExpr struct
type ListExpr (line 153) | type ListExpr struct
type ConcatExpr (line 163) | type ConcatExpr struct
type VarExpr (line 172) | type VarExpr struct
type IndexExpr (line 182) | type IndexExpr struct
type RedirectNode (line 193) | type RedirectNode struct
method SetMap (line 900) | func (r *RedirectNode) SetMap(lfd int, rfd int) {
method LeftFD (line 906) | func (r *RedirectNode) LeftFD() int { return r.rmap.lfd }
method RightFD (line 909) | func (r *RedirectNode) RightFD() int { return r.rmap.rfd }
method SetLocation (line 912) | func (r *RedirectNode) SetLocation(s Expr) { r.location = s }
method Location (line 915) | func (r *RedirectNode) Location() Expr { return r.location }
method IsEqual (line 918) | func (r *RedirectNode) IsEqual(other Node) bool {
type RforkNode (line 203) | type RforkNode struct
method Arg (line 952) | func (n *RforkNode) Arg() *StringExpr {
method SetFlags (line 957) | func (n *RforkNode) SetFlags(a *StringExpr) {
method Tree (line 962) | func (n *RforkNode) Tree() *Tree {
method SetTree (line 967) | func (n *RforkNode) SetTree(t *Tree) {
method IsEqual (line 971) | func (n *RforkNode) IsEqual(other Node) bool {
type CommentNode (line 213) | type CommentNode struct
method IsEqual (line 1005) | func (n *CommentNode) IsEqual(other Node) bool {
type RedirMap (line 222) | type RedirMap struct
type IfNode (line 228) | type IfNode struct
method Lvalue (line 1032) | func (n *IfNode) Lvalue() Expr {
method Rvalue (line 1037) | func (n *IfNode) Rvalue() Expr {
method SetLvalue (line 1042) | func (n *IfNode) SetLvalue(arg Expr) {
method SetRvalue (line 1047) | func (n *IfNode) SetRvalue(arg Expr) {
method Op (line 1052) | func (n *IfNode) Op() string { return n.op }
method SetOp (line 1055) | func (n *IfNode) SetOp(op string) {
method IsElseIf (line 1060) | func (n *IfNode) IsElseIf() bool {
method SetElseif (line 1065) | func (n *IfNode) SetElseif(b bool) {
method SetIfTree (line 1070) | func (n *IfNode) SetIfTree(t *Tree) {
method SetElseTree (line 1075) | func (n *IfNode) SetElseTree(t *Tree) {
method IfTree (line 1080) | func (n *IfNode) IfTree() *Tree { return n.ifTree }
method ElseTree (line 1083) | func (n *IfNode) ElseTree() *Tree { return n.elseTree }
method IsEqual (line 1086) | func (n *IfNode) IsEqual(other Node) bool {
type VarAssignDeclNode (line 243) | type VarAssignDeclNode struct
method IsEqual (line 1165) | func (n *VarAssignDeclNode) IsEqual(other Node) bool {
type VarExecAssignDeclNode (line 252) | type VarExecAssignDeclNode struct
method IsEqual (line 1185) | func (n *VarExecAssignDeclNode) IsEqual(other Node) bool {
type FnArgNode (line 261) | type FnArgNode struct
method IsEqual (line 1143) | func (a *FnArgNode) IsEqual(other Node) bool {
type FnDeclNode (line 271) | type FnDeclNode struct
method SetName (line 1209) | func (n *FnDeclNode) SetName(a string) {
method Name (line 1214) | func (n *FnDeclNode) Name() string {
method Args (line 1219) | func (n *FnDeclNode) Args() []*FnArgNode {
method AddArg (line 1224) | func (n *FnDeclNode) AddArg(arg *FnArgNode) {
method Tree (line 1229) | func (n *FnDeclNode) Tree() *Tree {
method SetTree (line 1234) | func (n *FnDeclNode) SetTree(t *Tree) {
method IsEqual (line 1238) | func (n *FnDeclNode) IsEqual(other Node) bool {
type FnInvNode (line 282) | type FnInvNode struct
method SetName (line 1273) | func (n *FnInvNode) SetName(a string) {
method Name (line 1278) | func (n *FnInvNode) Name() string {
method AddArg (line 1283) | func (n *FnInvNode) AddArg(arg Expr) {
method Args (line 1288) | func (n *FnInvNode) Args() []Expr { return n.args }
method IsEqual (line 1291) | func (n *FnInvNode) IsEqual(other Node) bool {
type ReturnNode (line 292) | type ReturnNode struct
method IsEqual (line 1354) | func (n *ReturnNode) IsEqual(other Node) bool {
type BindFnNode (line 301) | type BindFnNode struct
method Name (line 1327) | func (n *BindFnNode) Name() string { return n.name }
method CmdName (line 1330) | func (n *BindFnNode) CmdName() string { return n.cmdname }
method IsEqual (line 1332) | func (n *BindFnNode) IsEqual(other Node) bool {
type ForNode (line 311) | type ForNode struct
method SetIdentifier (line 1394) | func (n *ForNode) SetIdentifier(a string) {
method Identifier (line 1399) | func (n *ForNode) Identifier() string { return n.identifier }
method InExpr (line 1402) | func (n *ForNode) InExpr() Expr { return n.inExpr }
method SetInExpr (line 1405) | func (n *ForNode) SetInExpr(a Expr) { n.inExpr = a }
method SetTree (line 1408) | func (n *ForNode) SetTree(a *Tree) {
method Tree (line 1413) | func (n *ForNode) Tree() *Tree { return n.tree }
method IsEqual (line 1415) | func (n *ForNode) IsEqual(other Node) bool {
constant NodeSetenv (line 326) | NodeSetenv NodeType = iota + 1
constant NodeBlock (line 329) | NodeBlock
constant NodeName (line 332) | NodeName
constant NodeAssign (line 335) | NodeAssign
constant NodeExecAssign (line 338) | NodeExecAssign
constant NodeImport (line 341) | NodeImport
constant execBegin (line 343) | execBegin
constant NodeCommand (line 346) | NodeCommand
constant NodePipe (line 349) | NodePipe
constant NodeRedirect (line 352) | NodeRedirect
constant NodeFnInv (line 355) | NodeFnInv
constant execEnd (line 357) | execEnd
constant expressionBegin (line 359) | expressionBegin
constant NodeStringExpr (line 362) | NodeStringExpr
constant NodeIntExpr (line 365) | NodeIntExpr
constant NodeVarExpr (line 368) | NodeVarExpr
constant NodeListExpr (line 371) | NodeListExpr
constant NodeIndexExpr (line 374) | NodeIndexExpr
constant NodeConcatExpr (line 377) | NodeConcatExpr
constant expressionEnd (line 379) | expressionEnd
constant NodeString (line 382) | NodeString
constant NodeRfork (line 385) | NodeRfork
constant NodeRforkFlags (line 388) | NodeRforkFlags
constant NodeIf (line 391) | NodeIf
constant NodeComment (line 394) | NodeComment
constant NodeFnArg (line 396) | NodeFnArg
constant NodeVarAssignDecl (line 399) | NodeVarAssignDecl
constant NodeVarExecAssignDecl (line 402) | NodeVarExecAssignDecl
constant NodeFnDecl (line 405) | NodeFnDecl
constant NodeReturn (line 408) | NodeReturn
constant NodeBindFn (line 411) | NodeBindFn
constant NodeFor (line 414) | NodeFor
function debug (line 421) | func debug(format string, args ...interface{}) {
function NewBlockNode (line 459) | func NewBlockNode(info token.FileInfo) *BlockNode {
function NewImportNode (line 500) | func NewImportNode(info token.FileInfo, path *StringExpr) *ImportNode {
function NewSetenvNode (line 532) | func NewSetenvNode(info token.FileInfo, name string, assign Node) (*Sete...
function NewNameNode (line 572) | func NewNameNode(info token.FileInfo, ident string, index Expr) *NameNode {
function NewAssignNode (line 611) | func NewAssignNode(info token.FileInfo, names []*NameNode, values []Expr...
function NewSingleAssignNode (line 626) | func NewSingleAssignNode(info token.FileInfo, name *NameNode, value Expr...
function NewExecAssignNode (line 680) | func NewExecAssignNode(info token.FileInfo, names []*NameNode, n Node) (...
function NewCommandNode (line 744) | func NewCommandNode(info token.FileInfo, name string, multiline bool) *C...
function NewPipeNode (line 834) | func NewPipeNode(info token.FileInfo, multi bool) *PipeNode {
function NewRedirectNode (line 887) | func NewRedirectNode(info token.FileInfo) *RedirectNode {
function NewRforkNode (line 944) | func NewRforkNode(info token.FileInfo) *RforkNode {
function NewCommentNode (line 996) | func NewCommentNode(info token.FileInfo, val string) *CommentNode {
function NewIfNode (line 1024) | func NewIfNode(info token.FileInfo) *IfNode {
function NewFnArgNode (line 1133) | func NewFnArgNode(info token.FileInfo, name string, isVariadic bool) *Fn...
function NewVarAssignDecl (line 1158) | func NewVarAssignDecl(info token.FileInfo, assignNode *AssignNode) *VarA...
function NewVarExecAssignDecl (line 1178) | func NewVarExecAssignDecl(info token.FileInfo, assignNode *ExecAssignNod...
function NewFnDeclNode (line 1199) | func NewFnDeclNode(info token.FileInfo, name string) *FnDeclNode {
function NewFnInvNode (line 1263) | func NewFnInvNode(info token.FileInfo, name string) *FnInvNode {
function NewBindFnNode (line 1316) | func NewBindFnNode(info token.FileInfo, name, cmd string) *BindFnNode {
function NewReturnNode (line 1347) | func NewReturnNode(info token.FileInfo) *ReturnNode {
function NewForNode (line 1386) | func NewForNode(info token.FileInfo) *ForNode {
function cmpInfo (line 1445) | func cmpInfo(n, other Node) bool {
FILE: ast/node_args.go
function ExprFromToken (line 11) | func ExprFromToken(val scanner.Token) (Expr, error) {
function NewStringExpr (line 25) | func NewStringExpr(info token.FileInfo, value string, quoted bool) *Stri...
method Value (line 36) | func (s *StringExpr) Value() string {
method SetValue (line 40) | func (s *StringExpr) SetValue(a string) {
method IsEqual (line 44) | func (s *StringExpr) IsEqual(other Node) bool {
function NewIntExpr (line 62) | func NewIntExpr(info token.FileInfo, val int) *IntExpr {
method Value (line 71) | func (i *IntExpr) Value() int { return i.val }
method IsEqual (line 73) | func (i *IntExpr) IsEqual(other Node) bool {
function NewListExpr (line 87) | func NewListExpr(info token.FileInfo, values []Expr) *ListExpr {
function NewListVariadicExpr (line 91) | func NewListVariadicExpr(info token.FileInfo, values []Expr, variadic bo...
method PushExpr (line 102) | func (l *ListExpr) PushExpr(a Expr) {
method IsEqual (line 106) | func (l *ListExpr) IsEqual(other Node) bool {
function NewConcatExpr (line 133) | func NewConcatExpr(info token.FileInfo, parts []Expr) *ConcatExpr {
method PushExpr (line 143) | func (c *ConcatExpr) PushExpr(a Expr) {
method SetConcat (line 148) | func (c *ConcatExpr) SetConcat(v []Expr) {
method List (line 152) | func (c *ConcatExpr) List() []Expr { return c.concat }
method IsEqual (line 154) | func (c *ConcatExpr) IsEqual(other Node) bool {
function NewVarExpr (line 178) | func NewVarExpr(info token.FileInfo, name string) *VarExpr {
function NewVarVariadicExpr (line 182) | func NewVarVariadicExpr(info token.FileInfo, name string, isVariadic boo...
method IsEqual (line 191) | func (v *VarExpr) IsEqual(other Node) bool {
function NewIndexExpr (line 205) | func NewIndexExpr(info token.FileInfo, va *VarExpr, idx Expr) *IndexExpr {
function NewIndexVariadicExpr (line 209) | func NewIndexVariadicExpr(info token.FileInfo, va *VarExpr, idx Expr, va...
method IsEqual (line 220) | func (i *IndexExpr) IsEqual(other Node) bool {
FILE: ast/node_fmt.go
method String (line 9) | func (s *StringExpr) String() string {
method String (line 17) | func (i *IntExpr) String() string {
method string (line 21) | func (l *ListExpr) string() (string, bool) {
method String (line 43) | func (l *ListExpr) String() string {
method String (line 48) | func (c *ConcatExpr) String() string {
method String (line 62) | func (v *VarExpr) String() string {
method String (line 69) | func (i *IndexExpr) String() string {
method adjustGroupAssign (line 77) | func (l *BlockNode) adjustGroupAssign(node assignable, nodes []Node) {
method String (line 107) | func (l *BlockNode) String() string {
method String (line 158) | func (n *ImportNode) String() string {
method String (line 163) | func (n *SetenvNode) String() string {
method String (line 171) | func (n *NameNode) String() string {
method string (line 179) | func (n *AssignNode) string() (string, bool) {
method String (line 227) | func (n *AssignNode) String() string {
method string (line 232) | func (n *ExecAssignNode) string() (string, bool) {
method String (line 260) | func (n *ExecAssignNode) String() string {
method toStringParts (line 265) | func (n *CommandNode) toStringParts() ([]string, int) {
method multiString (line 336) | func (n *CommandNode) multiString() string {
method string (line 356) | func (n *CommandNode) string() (string, bool) {
method String (line 376) | func (n *CommandNode) String() string {
method multiString (line 381) | func (n *PipeNode) multiString() string {
method string (line 441) | func (n *PipeNode) string() (string, bool) {
method String (line 459) | func (n *PipeNode) String() string {
method String (line 465) | func (r *RedirectNode) String() string {
method String (line 492) | func (n *RforkNode) String() string {
method String (line 512) | func (n *CommentNode) String() string {
method String (line 517) | func (n *IfNode) String() string {
method String (line 566) | func (n *VarAssignDeclNode) String() string { return "var " + n.Assi...
method String (line 567) | func (n *VarExecAssignDeclNode) String() string { return "var " + n.Exec...
method String (line 570) | func (n *FnDeclNode) String() string {
method String (line 603) | func (arg *FnArgNode) String() string {
method string (line 612) | func (n *FnInvNode) string() (string, bool) {
method String (line 628) | func (n *FnInvNode) String() string {
method String (line 634) | func (n *BindFnNode) String() string {
method String (line 639) | func (n *ReturnNode) String() string {
method String (line 658) | func (n *ForNode) String() string {
function stringify (line 684) | func stringify(s string) string {
function getlhs (line 707) | func getlhs(node assignable) string {
FILE: ast/node_fmt_test.go
function testPrinter (line 9) | func testPrinter(t *testing.T, node Node, expected string) {
function TestAstPrinterStringExpr (line 15) | func TestAstPrinterStringExpr(t *testing.T) {
function TestASTPrinterAssignment (line 70) | func TestASTPrinterAssignment(t *testing.T) {
FILE: ast/nodetype_string.go
constant _NodeType_name (line 7) | _NodeType_name = "NodeSetenvNodeBlockNodeNameNodeAssignNodeExecAssignNod...
method String (line 11) | func (i NodeType) String() string {
FILE: ast/tree.go
type Tree (line 5) | type Tree struct
method IsEqual (line 18) | func (t *Tree) IsEqual(other *Tree) bool {
method String (line 26) | func (tree *Tree) String() string {
function NewTree (line 12) | func NewTree(name string) *Tree {
FILE: ast/tree_test.go
function TestTreeCreation (line 10) | func TestTreeCreation(t *testing.T) {
function TestTreeRawCreation (line 19) | func TestTreeRawCreation(t *testing.T) {
FILE: cmd/nash/cli.go
type Interrupted (line 18) | type Interrupted interface
type Ignored (line 22) | type Ignored interface
type BlockNotFinished (line 26) | type BlockNotFinished interface
function execFn (line 33) | func execFn(shell *nash.Shell, fnDef sh.FnDef, args []sh.Obj) {
function importInitFile (line 54) | func importInitFile(shell *nash.Shell, initFile string) (bool, error) {
function loadInit (line 68) | func loadInit(shell *nash.Shell) error {
function cli (line 92) | func cli(shell *nash.Shell) error {
function docli (line 136) | func docli(shell *nash.Shell, rline *readline.Instance) error {
FILE: cmd/nash/completer.go
type Completer (line 15) | type Completer struct
method Do (line 25) | func (c *Completer) Do(line []rune, pos int) ([][]rune, int) {
function NewCompleter (line 21) | func NewCompleter(op *readline.Operation, term *readline.Terminal, sh *n...
FILE: cmd/nash/env.go
function NashPath (line 9) | func NashPath() (string, error) {
function NashRoot (line 18) | func NashRoot() (string, error) {
function home (line 28) | func home() (string, error) {
FILE: cmd/nash/env_test.go
function TestLoadNASHPATH (line 14) | func TestLoadNASHPATH(t *testing.T) {
function TestLoadNASHROOT (line 45) | func TestLoadNASHROOT(t *testing.T) {
function runTests (line 81) | func runTests(t *testing.T, testfunc func() (string, error), cases []Env...
type EnvTest (line 102) | type EnvTest struct
function clearenv (line 108) | func clearenv(t *testing.T) func() {
function export (line 126) | func export(t *testing.T, env map[string]string) {
function home (line 137) | func home(t *testing.T) string {
FILE: cmd/nash/install.go
function NashLibDir (line 11) | func NashLibDir(nashpath string) string {
function InstallLib (line 16) | func InstallLib(nashpath string, sourcepath string) error {
function installLib (line 29) | func installLib(targetdir string, sourcepath string) error {
function copyfile (line 56) | func copyfile(targetdir string, sourcefilepath string) error {
FILE: cmd/nash/install_test.go
function TestInstallLib (line 15) | func TestInstallLib(t *testing.T) {
function TestSourcePathCantBeEqualToNashLibDir (line 188) | func TestSourcePathCantBeEqualToNashLibDir(t *testing.T) {
function TestSourcePathCantBeInsideNashLibDir (line 199) | func TestSourcePathCantBeInsideNashLibDir(t *testing.T) {
function TestRelativeSourcePathCantBeInsideNashLibDir (line 210) | func TestRelativeSourcePathCantBeInsideNashLibDir(t *testing.T) {
function TestFailsOnUnexistentSourcePath (line 225) | func TestFailsOnUnexistentSourcePath(t *testing.T) {
function TestFailsOnUnreadableSourcePath (line 232) | func TestFailsOnUnreadableSourcePath(t *testing.T) {
function TestFailsOnUnreadableFileInsideSourcePath (line 243) | func TestFailsOnUnreadableFileInsideSourcePath(t *testing.T) {
function TestFailsOnUnwriteableNashPath (line 259) | func TestFailsOnUnwriteableNashPath(t *testing.T) {
function TestFailsOnUnwriteableFileInsideNashLibdir (line 272) | func TestFailsOnUnwriteableFileInsideNashLibdir(t *testing.T) {
function assertInstallLibFails (line 293) | func assertInstallLibFails(t *testing.T, nashpath string, sourcepath str...
constant writeOnly (line 302) | writeOnly = 0333
constant readOnly (line 303) | readOnly = 0555
FILE: cmd/nash/main.go
function init (line 31) | func init() {
function main (line 45) | func main() {
function initShell (line 117) | func initShell() (*nash.Shell, error) {
FILE: cmd/nash/rpc.go
function serveConn (line 12) | func serveConn(sh *nash.Shell, conn net.Conn) {
function startNashd (line 54) | func startNashd(sh *nash.Shell, socketPath string) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/ansi_windows.go
constant _ (line 16) | _ = uint16(0)
constant COLOR_FBLUE (line 17) | COLOR_FBLUE = 0x0001
constant COLOR_FGREEN (line 18) | COLOR_FGREEN = 0x0002
constant COLOR_FRED (line 19) | COLOR_FRED = 0x0004
constant COLOR_FINTENSITY (line 20) | COLOR_FINTENSITY = 0x0008
constant COLOR_BBLUE (line 22) | COLOR_BBLUE = 0x0010
constant COLOR_BGREEN (line 23) | COLOR_BGREEN = 0x0020
constant COLOR_BRED (line 24) | COLOR_BRED = 0x0040
constant COLOR_BINTENSITY (line 25) | COLOR_BINTENSITY = 0x0080
constant COMMON_LVB_UNDERSCORE (line 27) | COMMON_LVB_UNDERSCORE = 0x8000
type ANSIWriter (line 52) | type ANSIWriter struct
method Close (line 67) | func (a *ANSIWriter) Close() error {
method Write (line 193) | func (a *ANSIWriter) Write(b []byte) (int, error) {
function NewANSIWriter (line 59) | func NewANSIWriter(w io.Writer) *ANSIWriter {
type ANSIWriterCtx (line 72) | type ANSIWriterCtx struct
method Flush (line 86) | func (a *ANSIWriterCtx) Flush() {
method process (line 90) | func (a *ANSIWriterCtx) process(r rune) bool {
method ioloopEscSeq (line 120) | func (a *ANSIWriterCtx) ioloopEscSeq(w *bufio.Writer, r rune, argptr *...
function NewANSIWriterCtx (line 80) | func NewANSIWriterCtx(target io.Writer) *ANSIWriterCtx {
function killLines (line 210) | func killLines() error {
function eraseLine (line 232) | func eraseLine() error {
FILE: cmd/nash/vendor/github.com/chzyer/readline/complete.go
type AutoCompleter (line 10) | type AutoCompleter interface
type TabCompleter (line 21) | type TabCompleter struct
method Do (line 23) | func (t *TabCompleter) Do([]rune, int) ([][]rune, int) {
type opCompleter (line 27) | type opCompleter struct
method doSelect (line 49) | func (o *opCompleter) doSelect() {
method nextCandidate (line 59) | func (o *opCompleter) nextCandidate(i int) {
method OnComplete (line 67) | func (o *opCompleter) OnComplete() bool {
method IsInCompleteSelectMode (line 118) | func (o *opCompleter) IsInCompleteSelectMode() bool {
method IsInCompleteMode (line 122) | func (o *opCompleter) IsInCompleteMode() bool {
method HandleCompleteSelect (line 126) | func (o *opCompleter) HandleCompleteSelect(r rune) bool {
method getMatrixSize (line 181) | func (o *opCompleter) getMatrixSize() int {
method OnWidthChange (line 189) | func (o *opCompleter) OnWidthChange(newWidth int) {
method CompleteRefresh (line 193) | func (o *opCompleter) CompleteRefresh() {
method aggCandidate (line 247) | func (o *opCompleter) aggCandidate(candidate [][]rune) int {
method EnterCompleteSelectMode (line 264) | func (o *opCompleter) EnterCompleteSelectMode() {
method EnterCompleteMode (line 270) | func (o *opCompleter) EnterCompleteMode(offset int, candidate [][]rune) {
method ExitCompleteSelectMode (line 277) | func (o *opCompleter) ExitCompleteSelectMode() {
method ExitCompleteMode (line 285) | func (o *opCompleter) ExitCompleteMode(revent bool) {
function newOpCompleter (line 41) | func newOpCompleter(w io.Writer, op *Operation, width int) *opCompleter {
FILE: cmd/nash/vendor/github.com/chzyer/readline/complete_helper.go
type DynamicCompleteFunc (line 9) | type DynamicCompleteFunc
type PrefixCompleterInterface (line 11) | type PrefixCompleterInterface interface
type DynamicPrefixCompleterInterface (line 19) | type DynamicPrefixCompleterInterface interface
type PrefixCompleter (line 25) | type PrefixCompleter struct
method Tree (line 32) | func (p *PrefixCompleter) Tree(prefix string) string {
method Print (line 54) | func (p *PrefixCompleter) Print(prefix string, level int, buf *bytes.B...
method IsDynamic (line 58) | func (p *PrefixCompleter) IsDynamic() bool {
method GetName (line 62) | func (p *PrefixCompleter) GetName() []rune {
method GetDynamicNames (line 66) | func (p *PrefixCompleter) GetDynamicNames(line []rune) [][]rune {
method GetChildren (line 74) | func (p *PrefixCompleter) GetChildren() []PrefixCompleterInterface {
method SetChildren (line 78) | func (p *PrefixCompleter) SetChildren(children []PrefixCompleterInterf...
method Do (line 103) | func (p *PrefixCompleter) Do(line []rune, pos int) (newLine [][]rune, ...
function Print (line 38) | func Print(p PrefixCompleterInterface, prefix string, level int, buf *by...
function NewPrefixCompleter (line 82) | func NewPrefixCompleter(pc ...PrefixCompleterInterface) *PrefixCompleter {
function PcItem (line 86) | func PcItem(name string, pc ...PrefixCompleterInterface) *PrefixCompleter {
function PcItemDynamic (line 95) | func PcItemDynamic(callback DynamicCompleteFunc, pc ...PrefixCompleterIn...
function Do (line 107) | func Do(p PrefixCompleterInterface, line []rune, pos int) (newLine [][]r...
function doInternal (line 111) | func doInternal(p PrefixCompleterInterface, line []rune, pos int, origLi...
FILE: cmd/nash/vendor/github.com/chzyer/readline/complete_segment.go
type SegmentCompleter (line 3) | type SegmentCompleter interface
type dumpSegmentCompleter (line 20) | type dumpSegmentCompleter struct
method DoSegment (line 24) | func (d *dumpSegmentCompleter) DoSegment(segment [][]rune, n int) [][]...
function SegmentFunc (line 28) | func SegmentFunc(f func([][]rune, int) [][]rune) AutoCompleter {
function SegmentAutoComplete (line 32) | func SegmentAutoComplete(completer SegmentCompleter) *SegmentComplete {
type SegmentComplete (line 38) | type SegmentComplete struct
method Do (line 72) | func (c *SegmentComplete) Do(line []rune, pos int) (newLine [][]rune, ...
function RetSegment (line 42) | func RetSegment(segments [][]rune, cands [][]rune, idx int) ([][]rune, i...
function SplitSegment (line 54) | func SplitSegment(line []rune, pos int) ([][]rune, int) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/complete_segment_test.go
function rs (line 10) | func rs(s [][]rune) []string {
function sr (line 18) | func sr(s ...string) [][]rune {
function TestRetSegment (line 26) | func TestRetSegment(t *testing.T) {
function TestSplitSegment (line 58) | func TestSplitSegment(t *testing.T) {
type Tree (line 88) | type Tree struct
function TestSegmentCompleter (line 93) | func TestSegmentCompleter(t *testing.T) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-demo/readline-demo.go
function usage (line 15) | func usage(w io.Writer) {
function listFiles (line 21) | func listFiles(path string) func(string) []string {
function main (line 64) | func main() {
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-im/readline-im.go
function main (line 12) | func main() {
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-multiline/readline-multiline.go
function main (line 9) | func main() {
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-pass-strength/readline-pass-strength.go
constant Cyan (line 30) | Cyan = 36
constant Green (line 31) | Green = 32
constant Magenta (line 32) | Magenta = 35
constant Red (line 33) | Red = 31
constant Yellow (line 34) | Yellow = 33
constant BackgroundRed (line 35) | BackgroundRed = 41
function ColorEscape (line 42) | func ColorEscape(color int) string {
function Colorize (line 47) | func Colorize(msg string, color int) string {
function createStrengthPrompt (line 51) | func createStrengthPrompt(password []rune) string {
function main (line 82) | func main() {
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-remote/readline-remote-client/client.go
function main (line 5) | func main() {
FILE: cmd/nash/vendor/github.com/chzyer/readline/example/readline-remote/readline-remote-server/server.go
function main (line 9) | func main() {
FILE: cmd/nash/vendor/github.com/chzyer/readline/history.go
type hisItem (line 12) | type hisItem struct
method Clean (line 18) | func (h *hisItem) Clean() {
type opHistory (line 23) | type opHistory struct
method Reset (line 40) | func (o *opHistory) Reset() {
method IsHistoryClosed (line 45) | func (o *opHistory) IsHistoryClosed() bool {
method Init (line 51) | func (o *opHistory) Init() {
method initHistory (line 57) | func (o *opHistory) initHistory() {
method historyUpdatePath (line 64) | func (o *opHistory) historyUpdatePath(path string) {
method Compact (line 95) | func (o *opHistory) Compact() {
method Rewrite (line 101) | func (o *opHistory) Rewrite() {
method rewriteLocked (line 107) | func (o *opHistory) rewriteLocked() {
method Close (line 137) | func (o *opHistory) Close() {
method FindBck (line 145) | func (o *opHistory) FindBck(isNewSearch bool, rs []rune, start int) (i...
method FindFwd (line 165) | func (o *opHistory) FindFwd(isNewSearch bool, rs []rune, start int) (i...
method showItem (line 193) | func (o *opHistory) showItem(obj interface{}) []rune {
method Prev (line 201) | func (o *opHistory) Prev() []rune {
method Next (line 213) | func (o *opHistory) Next() ([]rune, bool) {
method debug (line 226) | func (o *opHistory) debug() {
method New (line 234) | func (o *opHistory) New(current []rune) (err error) {
method Revert (line 278) | func (o *opHistory) Revert() {
method Update (line 283) | func (o *opHistory) Update(s []rune, commit bool) (err error) {
method Push (line 308) | func (o *opHistory) Push(s []rune) {
function newOpHistory (line 32) | func newOpHistory(cfg *Config) (o *opHistory) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/operation.go
type InterruptError (line 12) | type InterruptError struct
method Error (line 16) | func (*InterruptError) Error() string {
type Operation (line 20) | type Operation struct
method SetPrompt (line 81) | func (o *Operation) SetPrompt(s string) {
method SetMaskRune (line 85) | func (o *Operation) SetMaskRune(r rune) {
method ioloop (line 89) | func (o *Operation) ioloop() {
method Stderr (line 296) | func (o *Operation) Stderr() io.Writer {
method Stdout (line 300) | func (o *Operation) Stdout() io.Writer {
method String (line 304) | func (o *Operation) String() (string, error) {
method Runes (line 309) | func (o *Operation) Runes() ([]rune, error) {
method PasswordEx (line 330) | func (o *Operation) PasswordEx(prompt string, l Listener) ([]byte, err...
method GenPasswordConfig (line 337) | func (o *Operation) GenPasswordConfig() *Config {
method PasswordWithConfig (line 341) | func (o *Operation) PasswordWithConfig(cfg *Config) ([]byte, error) {
method Password (line 349) | func (o *Operation) Password(prompt string) ([]byte, error) {
method SetTitle (line 353) | func (o *Operation) SetTitle(t string) {
method Slice (line 357) | func (o *Operation) Slice() ([]byte, error) {
method Close (line 365) | func (o *Operation) Close() {
method SetHistoryPath (line 369) | func (o *Operation) SetHistoryPath(path string) {
method IsNormalMode (line 377) | func (o *Operation) IsNormalMode() bool {
method SetConfig (line 381) | func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
method ResetHistory (line 412) | func (o *Operation) ResetHistory() {
method SaveHistory (line 418) | func (o *Operation) SaveHistory(content string) error {
method Refresh (line 422) | func (o *Operation) Refresh() {
method Clean (line 428) | func (o *Operation) Clean() {
type wrapWriter (line 34) | type wrapWriter struct
method Write (line 40) | func (w *wrapWriter) Write(b []byte) (int, error) {
function NewOperation (line 59) | func NewOperation(t *Terminal, cfg *Config) *Operation {
function FuncListener (line 432) | func FuncListener(f func(line []rune, pos int, key rune) (newLine []rune...
type DumpListener (line 436) | type DumpListener struct
method OnChange (line 440) | func (d *DumpListener) OnChange(line []rune, pos int, key rune) (newLi...
type Listener (line 444) | type Listener interface
FILE: cmd/nash/vendor/github.com/chzyer/readline/password.go
type opPassword (line 3) | type opPassword struct
method ExitPasswordMode (line 12) | func (o *opPassword) ExitPasswordMode() {
method EnterPasswordMode (line 17) | func (o *opPassword) EnterPasswordMode(cfg *Config) (err error) {
method PasswordConfig (line 22) | func (o *opPassword) PasswordConfig() *Config {
function newOpPassword (line 8) | func newOpPassword(o *Operation) *opPassword {
FILE: cmd/nash/vendor/github.com/chzyer/readline/rawreader_windows.go
constant VK_CANCEL (line 8) | VK_CANCEL = 0x03
constant VK_BACK (line 9) | VK_BACK = 0x08
constant VK_TAB (line 10) | VK_TAB = 0x09
constant VK_RETURN (line 11) | VK_RETURN = 0x0D
constant VK_SHIFT (line 12) | VK_SHIFT = 0x10
constant VK_CONTROL (line 13) | VK_CONTROL = 0x11
constant VK_MENU (line 14) | VK_MENU = 0x12
constant VK_ESCAPE (line 15) | VK_ESCAPE = 0x1B
constant VK_LEFT (line 16) | VK_LEFT = 0x25
constant VK_UP (line 17) | VK_UP = 0x26
constant VK_RIGHT (line 18) | VK_RIGHT = 0x27
constant VK_DOWN (line 19) | VK_DOWN = 0x28
constant VK_DELETE (line 20) | VK_DELETE = 0x2E
constant VK_LSHIFT (line 21) | VK_LSHIFT = 0xA0
constant VK_RSHIFT (line 22) | VK_RSHIFT = 0xA1
constant VK_LCONTROL (line 23) | VK_LCONTROL = 0xA2
constant VK_RCONTROL (line 24) | VK_RCONTROL = 0xA3
type RawReader (line 29) | type RawReader struct
method Read (line 40) | func (r *RawReader) Read(buf []byte) (int, error) {
method writeEsc (line 112) | func (r *RawReader) writeEsc(b []byte, char rune) (int, error) {
method write (line 118) | func (r *RawReader) write(b []byte, char rune) (int, error) {
method Close (line 123) | func (r *RawReader) Close() error {
function NewRawReader (line 34) | func NewRawReader() *RawReader {
FILE: cmd/nash/vendor/github.com/chzyer/readline/readline.go
type Instance (line 22) | type Instance struct
method ResetHistory (line 163) | func (i *Instance) ResetHistory() {
method SetPrompt (line 167) | func (i *Instance) SetPrompt(s string) {
method SetMaskRune (line 171) | func (i *Instance) SetMaskRune(r rune) {
method SetHistoryPath (line 176) | func (i *Instance) SetHistoryPath(p string) {
method Stdout (line 181) | func (i *Instance) Stdout() io.Writer {
method Stderr (line 186) | func (i *Instance) Stderr() io.Writer {
method SetVimMode (line 191) | func (i *Instance) SetVimMode(on bool) {
method IsVimMode (line 195) | func (i *Instance) IsVimMode() bool {
method GenPasswordConfig (line 199) | func (i *Instance) GenPasswordConfig() *Config {
method ReadPasswordWithConfig (line 204) | func (i *Instance) ReadPasswordWithConfig(cfg *Config) ([]byte, error) {
method ReadPasswordEx (line 208) | func (i *Instance) ReadPasswordEx(prompt string, l Listener) ([]byte, ...
method ReadPassword (line 212) | func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
method Line (line 229) | func (i *Instance) Line() *Result {
method Readline (line 235) | func (i *Instance) Readline() (string, error) {
method SaveHistory (line 239) | func (i *Instance) SaveHistory(content string) error {
method ReadSlice (line 244) | func (i *Instance) ReadSlice() ([]byte, error) {
method Close (line 249) | func (i *Instance) Close() error {
method Clean (line 256) | func (i *Instance) Clean() {
method Write (line 260) | func (i *Instance) Write(b []byte) (int, error) {
method SetConfig (line 264) | func (i *Instance) SetConfig(cfg *Config) *Config {
method Refresh (line 275) | func (i *Instance) Refresh() {
type Config (line 28) | type Config struct
method useInteractive (line 78) | func (c *Config) useInteractive() bool {
method Init (line 85) | func (c *Config) Init() error {
method Clone (line 137) | func (c Config) Clone() *Config {
method SetListener (line 142) | func (c *Config) SetListener(f func(line []rune, pos int, key rune) (n...
function NewEx (line 146) | func NewEx(cfg *Config) (*Instance, error) {
function New (line 159) | func New(prompt string) (*Instance, error) {
type Result (line 216) | type Result struct
method CanContinue (line 221) | func (l *Result) CanContinue() bool {
method CanBreak (line 225) | func (l *Result) CanBreak() bool {
FILE: cmd/nash/vendor/github.com/chzyer/readline/readline_test.go
function TestRace (line 8) | func TestRace(t *testing.T) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/remote.go
type MsgType (line 15) | type MsgType
constant T_DATA (line 18) | T_DATA = MsgType(iota)
constant T_WIDTH (line 19) | T_WIDTH
constant T_WIDTH_REPORT (line 20) | T_WIDTH_REPORT
constant T_ISTTY_REPORT (line 21) | T_ISTTY_REPORT
constant T_RAW (line 22) | T_RAW
constant T_ERAW (line 23) | T_ERAW
constant T_EOF (line 24) | T_EOF
type RemoteSvr (line 27) | type RemoteSvr struct
method init (line 78) | func (r *RemoteSvr) init(buf *bufio.Reader) error {
method HandleConfig (line 102) | func (r *RemoteSvr) HandleConfig(cfg *Config) {
method IsTerminal (line 116) | func (r *RemoteSvr) IsTerminal() bool {
method checkEOF (line 120) | func (r *RemoteSvr) checkEOF() error {
method Read (line 127) | func (r *RemoteSvr) Read(b []byte) (int, error) {
method writeMsg (line 152) | func (r *RemoteSvr) writeMsg(m *Message) error {
method Write (line 159) | func (r *RemoteSvr) Write(b []byte) (int, error) {
method EnterRawMode (line 166) | func (r *RemoteSvr) EnterRawMode() error {
method ExitRawMode (line 170) | func (r *RemoteSvr) ExitRawMode() error {
method writeLoop (line 174) | func (r *RemoteSvr) writeLoop() {
method Close (line 192) | func (r *RemoteSvr) Close() {
method readLoop (line 199) | func (r *RemoteSvr) readLoop(buf *bufio.Reader) {
method GotIsTerminal (line 229) | func (r *RemoteSvr) GotIsTerminal(data []byte) {
method GotReportWidth (line 237) | func (r *RemoteSvr) GotReportWidth(data []byte) {
method GetWidth (line 244) | func (r *RemoteSvr) GetWidth() int {
type writeReply (line 42) | type writeReply struct
type writeCtx (line 47) | type writeCtx struct
function newWriteCtx (line 52) | func newWriteCtx(msg *Message) *writeCtx {
function NewRemoteSvr (line 59) | func NewRemoteSvr(conn net.Conn) (*RemoteSvr, error) {
type Message (line 250) | type Message struct
method WriteTo (line 275) | func (m *Message) WriteTo(w io.Writer) (int, error) {
function ReadMessage (line 255) | func ReadMessage(r io.Reader) (*Message, error) {
function NewMessage (line 271) | func NewMessage(t MsgType, data []byte) *Message {
type RemoteCli (line 286) | type RemoteCli struct
method MarkIsTerminal (line 305) | func (r *RemoteCli) MarkIsTerminal(is bool) {
method init (line 309) | func (r *RemoteCli) init() error {
method writeMsg (line 329) | func (r *RemoteCli) writeMsg(m *Message) error {
method Write (line 336) | func (r *RemoteCli) Write(b []byte) (int, error) {
method reportWidth (line 344) | func (r *RemoteCli) reportWidth() error {
method reportIsTerminal (line 356) | func (r *RemoteCli) reportIsTerminal() error {
method readLoop (line 376) | func (r *RemoteCli) readLoop() {
method ServeBy (line 394) | func (r *RemoteCli) ServeBy(source io.Reader) error {
method Close (line 413) | func (r *RemoteCli) Close() {
method Serve (line 417) | func (r *RemoteCli) Serve() error {
function NewRemoteCli (line 297) | func NewRemoteCli(conn net.Conn) (*RemoteCli, error) {
function ListenRemote (line 421) | func ListenRemote(n, addr string, cfg *Config, h func(*Instance), onList...
function HandleConn (line 448) | func HandleConn(cfg Config, conn net.Conn) (*Instance, error) {
function DialRemote (line 462) | func DialRemote(n, addr string) error {
FILE: cmd/nash/vendor/github.com/chzyer/readline/runebuf.go
type runeBufferBck (line 11) | type runeBufferBck struct
type RuneBuffer (line 16) | type RuneBuffer struct
method OnWidthChange (line 35) | func (r *RuneBuffer) OnWidthChange(newWidth int) {
method Backup (line 41) | func (r *RuneBuffer) Backup() {
method Restore (line 47) | func (r *RuneBuffer) Restore() {
method SetConfig (line 68) | func (r *RuneBuffer) SetConfig(cfg *Config) {
method SetMask (line 75) | func (r *RuneBuffer) SetMask(m rune) {
method CurrentWidth (line 81) | func (r *RuneBuffer) CurrentWidth(x int) int {
method PromptLen (line 87) | func (r *RuneBuffer) PromptLen() int {
method promptLen (line 94) | func (r *RuneBuffer) promptLen() int {
method RuneSlice (line 98) | func (r *RuneBuffer) RuneSlice(i int) []rune {
method Runes (line 112) | func (r *RuneBuffer) Runes() []rune {
method Pos (line 120) | func (r *RuneBuffer) Pos() int {
method Len (line 126) | func (r *RuneBuffer) Len() int {
method MoveToLineStart (line 132) | func (r *RuneBuffer) MoveToLineStart() {
method MoveBackward (line 141) | func (r *RuneBuffer) MoveBackward() {
method WriteString (line 150) | func (r *RuneBuffer) WriteString(s string) {
method WriteRune (line 154) | func (r *RuneBuffer) WriteRune(s rune) {
method WriteRunes (line 158) | func (r *RuneBuffer) WriteRunes(s []rune) {
method MoveForward (line 166) | func (r *RuneBuffer) MoveForward() {
method IsCursorInEnd (line 175) | func (r *RuneBuffer) IsCursorInEnd() bool {
method Replace (line 181) | func (r *RuneBuffer) Replace(ch rune) {
method Erase (line 187) | func (r *RuneBuffer) Erase() {
method Delete (line 194) | func (r *RuneBuffer) Delete() (success bool) {
method DeleteWord (line 205) | func (r *RuneBuffer) DeleteWord() {
method MoveToPrevWord (line 224) | func (r *RuneBuffer) MoveToPrevWord() (success bool) {
method KillFront (line 243) | func (r *RuneBuffer) KillFront() {
method Kill (line 256) | func (r *RuneBuffer) Kill() {
method Transpose (line 262) | func (r *RuneBuffer) Transpose() {
method MoveToNextWord (line 282) | func (r *RuneBuffer) MoveToNextWord() {
method MoveToEndWord (line 295) | func (r *RuneBuffer) MoveToEndWord() {
method BackEscapeWord (line 317) | func (r *RuneBuffer) BackEscapeWord() {
method Backspace (line 335) | func (r *RuneBuffer) Backspace() {
method MoveToLineEnd (line 346) | func (r *RuneBuffer) MoveToLineEnd() {
method LineCount (line 356) | func (r *RuneBuffer) LineCount(width int) int {
method MoveTo (line 364) | func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success ...
method isInLineEdge (line 393) | func (r *RuneBuffer) isInLineEdge() bool {
method getSplitByLine (line 401) | func (r *RuneBuffer) getSplitByLine(rs []rune) []string {
method IdxLine (line 405) | func (r *RuneBuffer) IdxLine(width int) int {
method idxLine (line 411) | func (r *RuneBuffer) idxLine(width int) int {
method CursorLineCount (line 419) | func (r *RuneBuffer) CursorLineCount() int {
method Refresh (line 423) | func (r *RuneBuffer) Refresh(f func()) {
method SetOffset (line 441) | func (r *RuneBuffer) SetOffset(offset string) {
method print (line 447) | func (r *RuneBuffer) print() {
method output (line 452) | func (r *RuneBuffer) output() []byte {
method Reset (line 485) | func (r *RuneBuffer) Reset() []rune {
method calWidth (line 492) | func (r *RuneBuffer) calWidth(m int) int {
method SetStyle (line 499) | func (r *RuneBuffer) SetStyle(start, end int, style string) {
method SetWithIdx (line 517) | func (r *RuneBuffer) SetWithIdx(idx int, buf []rune) {
method Set (line 524) | func (r *RuneBuffer) Set(buf []rune) {
method SetPrompt (line 528) | func (r *RuneBuffer) SetPrompt(prompt string) {
method cleanOutput (line 534) | func (r *RuneBuffer) cleanOutput(w io.Writer, idxLine int) {
method Clean (line 556) | func (r *RuneBuffer) Clean() {
method clean (line 562) | func (r *RuneBuffer) clean() {
method cleanWithIdxLine (line 566) | func (r *RuneBuffer) cleanWithIdxLine(idxLine int) {
function NewRuneBuffer (line 57) | func NewRuneBuffer(w io.Writer, prompt string, cfg *Config, width int) *...
FILE: cmd/nash/vendor/github.com/chzyer/readline/runes.go
type Runes (line 12) | type Runes struct
method EqualRune (line 14) | func (Runes) EqualRune(a, b rune, fold bool) bool {
method EqualRuneFold (line 32) | func (r Runes) EqualRuneFold(a, b rune) bool {
method EqualFold (line 36) | func (r Runes) EqualFold(a, b []rune) bool {
method Equal (line 50) | func (Runes) Equal(a, b []rune) bool {
method IndexAllBckEx (line 62) | func (rs Runes) IndexAllBckEx(r, sub []rune, fold bool) int {
method IndexAllBck (line 79) | func (rs Runes) IndexAllBck(r, sub []rune) int {
method IndexAll (line 84) | func (rs Runes) IndexAll(r, sub []rune) int {
method IndexAllEx (line 88) | func (rs Runes) IndexAllEx(r, sub []rune, fold bool) int {
method Index (line 107) | func (Runes) Index(r rune, rs []rune) int {
method ColorFilter (line 116) | func (Runes) ColorFilter(r []rune) []rune {
method Width (line 146) | func (Runes) Width(r rune) int {
method WidthAll (line 159) | func (Runes) WidthAll(r []rune) (length int) {
method Backspace (line 166) | func (Runes) Backspace(r []rune) []byte {
method Copy (line 170) | func (Runes) Copy(r []rune) []rune {
method HasPrefixFold (line 176) | func (Runes) HasPrefixFold(r, prefix []rune) bool {
method HasPrefix (line 183) | func (Runes) HasPrefix(r, prefix []rune) bool {
method Aggregate (line 190) | func (Runes) Aggregate(candicate [][]rune) (same []rune, size int) {
method TrimSpaceLeft (line 214) | func (Runes) TrimSpaceLeft(in []rune) []rune {
FILE: cmd/nash/vendor/github.com/chzyer/readline/runes/runes.go
function Equal (line 11) | func Equal(a, b []rune) bool {
function IndexAllBck (line 24) | func IndexAllBck(r, sub []rune) int {
function IndexAll (line 41) | func IndexAll(r, sub []rune) int {
function Index (line 60) | func Index(r rune, rs []rune) int {
function ColorFilter (line 69) | func ColorFilter(r []rune) []rune {
function Width (line 99) | func Width(r rune) int {
function WidthAll (line 109) | func WidthAll(r []rune) (length int) {
function Backspace (line 116) | func Backspace(r []rune) []byte {
function Copy (line 120) | func Copy(r []rune) []rune {
function HasPrefix (line 126) | func HasPrefix(r, prefix []rune) bool {
function Aggregate (line 133) | func Aggregate(candicate [][]rune) (same []rune, size int) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/runes/runes_test.go
type twidth (line 8) | type twidth struct
function TestRuneWidth (line 13) | func TestRuneWidth(t *testing.T) {
type tagg (line 27) | type tagg struct
function TestAggRunes (line 33) | func TestAggRunes(t *testing.T) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/runes_test.go
type twidth (line 8) | type twidth struct
function TestRuneWidth (line 13) | func TestRuneWidth(t *testing.T) {
type tagg (line 27) | type tagg struct
function TestAggRunes (line 33) | func TestAggRunes(t *testing.T) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/std.go
function getInstance (line 21) | func getInstance() *Instance {
function SetHistoryPath (line 34) | func SetHistoryPath(fp string) {
function SetAutoComplete (line 42) | func SetAutoComplete(completer AutoCompleter) {
function AddHistory (line 51) | func AddHistory(content string) error {
function Password (line 56) | func Password(prompt string) ([]byte, error) {
function Line (line 62) | func Line(prompt string) (string, error) {
type CancelableStdin (line 68) | type CancelableStdin struct
method ioloop (line 88) | func (c *CancelableStdin) ioloop() {
method Read (line 101) | func (c *CancelableStdin) Read(b []byte) (n int, err error) {
method Close (line 115) | func (c *CancelableStdin) Close() error {
function NewCancelableStdin (line 78) | func NewCancelableStdin(r io.Reader) *CancelableStdin {
FILE: cmd/nash/vendor/github.com/chzyer/readline/std_windows.go
function init (line 5) | func init() {
FILE: cmd/nash/vendor/github.com/chzyer/readline/term.go
type State (line 26) | type State struct
function IsTerminal (line 31) | func IsTerminal(fd int) bool {
function MakeRaw (line 40) | func MakeRaw(fd int) (*State, error) {
function GetState (line 64) | func GetState(fd int) (*State, error) {
function restoreTerm (line 75) | func restoreTerm(fd int, state *State) error {
function GetSize (line 81) | func GetSize(fd int) (width, height int, err error) {
function ReadPassword (line 93) | func ReadPassword(fd int) ([]byte, error) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/term_bsd.go
constant ioctlReadTermios (line 11) | ioctlReadTermios = syscall.TIOCGETA
constant ioctlWriteTermios (line 12) | ioctlWriteTermios = syscall.TIOCSETA
FILE: cmd/nash/vendor/github.com/chzyer/readline/term_linux.go
constant ioctlReadTermios (line 10) | ioctlReadTermios = 0x5401
constant ioctlWriteTermios (line 11) | ioctlWriteTermios = 0x5402
FILE: cmd/nash/vendor/github.com/chzyer/readline/term_windows.go
constant enableLineInput (line 26) | enableLineInput = 2
constant enableEchoInput (line 27) | enableEchoInput = 4
constant enableProcessedInput (line 28) | enableProcessedInput = 1
constant enableWindowInput (line 29) | enableWindowInput = 8
constant enableMouseInput (line 30) | enableMouseInput = 16
constant enableInsertMode (line 31) | enableInsertMode = 32
constant enableQuickEditMode (line 32) | enableQuickEditMode = 64
constant enableExtendedFlags (line 33) | enableExtendedFlags = 128
constant enableAutoPosition (line 34) | enableAutoPosition = 256
constant enableProcessedOutput (line 35) | enableProcessedOutput = 1
constant enableWrapAtEolOutput (line 36) | enableWrapAtEolOutput = 2
type coord (line 48) | type coord struct
type smallRect (line 52) | type smallRect struct
type consoleScreenBufferInfo (line 58) | type consoleScreenBufferInfo struct
type State (line 67) | type State struct
function IsTerminal (line 72) | func IsTerminal(fd int) bool {
function MakeRaw (line 81) | func MakeRaw(fd int) (*State, error) {
function GetState (line 97) | func GetState(fd int) (*State, error) {
function restoreTerm (line 108) | func restoreTerm(fd int, state *State) error {
function GetSize (line 114) | func GetSize(fd int) (width, height int, err error) {
function ReadPassword (line 126) | func ReadPassword(fd int) ([]byte, error) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/terminal.go
type Terminal (line 13) | type Terminal struct
method SleepToResume (line 44) | func (t *Terminal) SleepToResume() {
method EnterRawMode (line 57) | func (t *Terminal) EnterRawMode() (err error) {
method ExitRawMode (line 61) | func (t *Terminal) ExitRawMode() (err error) {
method Write (line 65) | func (t *Terminal) Write(b []byte) (int, error) {
method GetOffset (line 74) | func (t *Terminal) GetOffset(f func(offset string)) {
method Print (line 81) | func (t *Terminal) Print(s string) {
method PrintRune (line 85) | func (t *Terminal) PrintRune(r rune) {
method Readline (line 89) | func (t *Terminal) Readline() *Operation {
method ReadRune (line 94) | func (t *Terminal) ReadRune() rune {
method IsReading (line 102) | func (t *Terminal) IsReading() bool {
method KickRead (line 106) | func (t *Terminal) KickRead() {
method PauseRead (line 113) | func (t *Terminal) PauseRead(b bool) {
method ioloop (line 121) | func (t *Terminal) ioloop() {
method Bell (line 214) | func (t *Terminal) Bell() {
method Close (line 218) | func (t *Terminal) Close() error {
method SetConfig (line 230) | func (t *Terminal) SetConfig(c *Config) error {
function NewTerminal (line 27) | func NewTerminal(cfg *Config) (*Terminal, error) {
type termSize (line 69) | type termSize struct
FILE: cmd/nash/vendor/github.com/chzyer/readline/utils.go
constant CharLineStart (line 21) | CharLineStart = 1
constant CharBackward (line 22) | CharBackward = 2
constant CharInterrupt (line 23) | CharInterrupt = 3
constant CharDelete (line 24) | CharDelete = 4
constant CharLineEnd (line 25) | CharLineEnd = 5
constant CharForward (line 26) | CharForward = 6
constant CharBell (line 27) | CharBell = 7
constant CharCtrlH (line 28) | CharCtrlH = 8
constant CharTab (line 29) | CharTab = 9
constant CharCtrlJ (line 30) | CharCtrlJ = 10
constant CharKill (line 31) | CharKill = 11
constant CharCtrlL (line 32) | CharCtrlL = 12
constant CharEnter (line 33) | CharEnter = 13
constant CharNext (line 34) | CharNext = 14
constant CharPrev (line 35) | CharPrev = 16
constant CharBckSearch (line 36) | CharBckSearch = 18
constant CharFwdSearch (line 37) | CharFwdSearch = 19
constant CharTranspose (line 38) | CharTranspose = 20
constant CharCtrlU (line 39) | CharCtrlU = 21
constant CharCtrlW (line 40) | CharCtrlW = 23
constant CharCtrlZ (line 41) | CharCtrlZ = 26
constant CharEsc (line 42) | CharEsc = 27
constant CharEscapeEx (line 43) | CharEscapeEx = 91
constant CharBackspace (line 44) | CharBackspace = 127
constant MetaBackward (line 48) | MetaBackward rune = -iota - 1
constant MetaForward (line 49) | MetaForward
constant MetaDelete (line 50) | MetaDelete
constant MetaBackspace (line 51) | MetaBackspace
constant MetaTranspose (line 52) | MetaTranspose
function WaitForResume (line 58) | func WaitForResume() chan struct{} {
function Restore (line 80) | func Restore(fd int, state *State) error {
function IsPrintable (line 91) | func IsPrintable(key rune) bool {
function escapeExKey (line 97) | func escapeExKey(key *escapeKeyPair) rune {
type escapeKeyPair (line 121) | type escapeKeyPair struct
method Get2 (line 126) | func (e *escapeKeyPair) Get2() (int, int, bool) {
function readEscKey (line 142) | func readEscKey(r rune, reader *bufio.Reader) *escapeKeyPair {
function escapeKey (line 160) | func escapeKey(r rune, reader *bufio.Reader) rune {
function SplitByLine (line 188) | func SplitByLine(start, screenWidth int, rs []rune) []string {
function LineCount (line 207) | func LineCount(screenWidth, w int) int {
function IsWordBreak (line 215) | func IsWordBreak(i rune) bool {
function GetInt (line 226) | func GetInt(s []string, def int) int {
type RawMode (line 237) | type RawMode struct
method Enter (line 241) | func (r *RawMode) Enter() (err error) {
method Exit (line 246) | func (r *RawMode) Exit() error {
function sleep (line 255) | func sleep(n int) {
function debugList (line 261) | func debugList(l *list.List) {
function Debug (line 270) | func Debug(o ...interface{}) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/utils_unix.go
type winsize (line 14) | type winsize struct
function SuspendMe (line 24) | func SuspendMe() {
function getWidth (line 32) | func getWidth(stdoutFd int) int {
function GetScreenWidth (line 46) | func GetScreenWidth() int {
function ClearScreen (line 55) | func ClearScreen(w io.Writer) (int, error) {
function DefaultIsTerminal (line 59) | func DefaultIsTerminal() bool {
function GetStdin (line 63) | func GetStdin() int {
function DefaultOnWidthChanged (line 74) | func DefaultOnWidthChanged(f func()) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/utils_windows.go
function SuspendMe (line 10) | func SuspendMe() {
function GetStdin (line 13) | func GetStdin() int {
function init (line 17) | func init() {
function GetScreenWidth (line 22) | func GetScreenWidth() int {
function ClearScreen (line 31) | func ClearScreen(_ io.Writer) error {
function DefaultIsTerminal (line 35) | func DefaultIsTerminal() bool {
function DefaultOnWidthChanged (line 39) | func DefaultOnWidthChanged(func()) {
FILE: cmd/nash/vendor/github.com/chzyer/readline/vim.go
constant VIM_NORMAL (line 4) | VIM_NORMAL = iota
constant VIM_INSERT (line 5) | VIM_INSERT
constant VIM_VISUAL (line 6) | VIM_VISUAL
type opVim (line 9) | type opVim struct
method SetVimMode (line 24) | func (o *opVim) SetVimMode(on bool) {
method ExitVimMode (line 32) | func (o *opVim) ExitVimMode() {
method IsEnableVimMode (line 36) | func (o *opVim) IsEnableVimMode() bool {
method handleVimNormalMovement (line 40) | func (o *opVim) handleVimNormalMovement(r rune, readNext func() rune) ...
method handleVimNormalEnterInsert (line 96) | func (o *opVim) handleVimNormalEnterInsert(r rune, readNext func() run...
method HandleVimNormal (line 131) | func (o *opVim) HandleVimNormal(r rune, readNext func() rune) (t rune) {
method EnterVimInsertMode (line 151) | func (o *opVim) EnterVimInsertMode() {
method ExitVimInsertMode (line 155) | func (o *opVim) ExitVimInsertMode() {
method HandleVim (line 159) | func (o *opVim) HandleVim(r rune, readNext func() rune) rune {
function newVimMode (line 15) | func newVimMode(op *Operation) *opVim {
FILE: cmd/nash/vendor/github.com/chzyer/readline/windows_api.go
type Kernel (line 17) | type Kernel struct
method Wrap (line 105) | func (k *Kernel) Wrap(p *syscall.LazyProc) CallFunc {
type short (line 28) | type short
type word (line 29) | type word
type dword (line 30) | type dword
type wchar (line 31) | type wchar
type _COORD (line 33) | type _COORD struct
method ptr (line 38) | func (c *_COORD) ptr() uintptr {
constant EVENT_KEY (line 43) | EVENT_KEY = 0x0001
constant EVENT_MOUSE (line 44) | EVENT_MOUSE = 0x0002
constant EVENT_WINDOW_BUFFER_SIZE (line 45) | EVENT_WINDOW_BUFFER_SIZE = 0x0004
constant EVENT_MENU (line 46) | EVENT_MENU = 0x0008
constant EVENT_FOCUS (line 47) | EVENT_FOCUS = 0x0010
type _KEY_EVENT_RECORD (line 50) | type _KEY_EVENT_RECORD struct
type _INPUT_RECORD (line 64) | type _INPUT_RECORD struct
type _CONSOLE_SCREEN_BUFFER_INFO (line 70) | type _CONSOLE_SCREEN_BUFFER_INFO struct
type _SMALL_RECT (line 78) | type _SMALL_RECT struct
type _CONSOLE_CURSOR_INFO (line 85) | type _CONSOLE_CURSOR_INFO struct
type CallFunc (line 90) | type CallFunc
function NewKernel (line 92) | func NewKernel() *Kernel {
function GetConsoleScreenBufferInfo (line 135) | func GetConsoleScreenBufferInfo() (*_CONSOLE_SCREEN_BUFFER_INFO, error) {
function GetConsoleCursorInfo (line 144) | func GetConsoleCursorInfo() (*_CONSOLE_CURSOR_INFO, error) {
function SetConsoleCursorPosition (line 150) | func SetConsoleCursorPosition(c *_COORD) error {
FILE: cmd/nashfmt/main.go
function init (line 20) | func init() {
function main (line 25) | func main() {
FILE: errors/error.go
type NashError (line 11) | type NashError struct
method SetReason (line 45) | func (e *NashError) SetReason(format string, arg ...interface{}) {
method Error (line 49) | func (e *NashError) Error() string { return e.reason }
type unfinished (line 16) | type unfinished struct
method Unfinished (line 51) | func (e unfinished) Unfinished() bool { return true }
type unfinishedBlockError (line 18) | type unfinishedBlockError struct
type unfinishedListError (line 23) | type unfinishedListError struct
type unfinishedCmdError (line 28) | type unfinishedCmdError struct
function NewError (line 34) | func NewError(format string, arg ...interface{}) *NashError {
function NewEvalError (line 40) | func NewEvalError(path string, node ast.Node, format string, arg ...inte...
function NewUnfinishedBlockError (line 53) | func NewUnfinishedBlockError(name string, it scanner.Token) error {
function NewUnfinishedListError (line 60) | func NewUnfinishedListError(name string, it scanner.Token) error {
function NewUnfinishedCmdError (line 67) | func NewUnfinishedCmdError(name string, it scanner.Token) error {
FILE: examples_test.go
function Example (line 10) | func Example() {
function tmpdir (line 34) | func tmpdir() (string, func()) {
FILE: fuzz.go
function Fuzz (line 7) | func Fuzz(data []byte) int {
FILE: internal/sh/builtin.go
type builtinFn (line 18) | type builtinFn struct
method Name (line 47) | func (f *builtinFn) Name() string {
method ArgNames (line 51) | func (f *builtinFn) ArgNames() []sh.FnArg {
method Start (line 55) | func (f *builtinFn) Start() error {
method Wait (line 66) | func (f *builtinFn) Wait() error {
method Results (line 71) | func (f *builtinFn) Results() []sh.Obj {
method String (line 75) | func (f *builtinFn) String() string {
method SetArgs (line 79) | func (f *builtinFn) SetArgs(args []sh.Obj) error {
method SetEnviron (line 83) | func (f *builtinFn) SetEnviron(env []string) {
method SetStdin (line 88) | func (f *builtinFn) SetStdin(r io.Reader) {
method SetStderr (line 91) | func (f *builtinFn) SetStderr(w io.Writer) {
method SetStdout (line 94) | func (f *builtinFn) SetStdout(w io.Writer) {
method StdoutPipe (line 97) | func (f *builtinFn) StdoutPipe() (io.ReadCloser, error) {
method Stdin (line 100) | func (f *builtinFn) Stdin() io.Reader { return f.stdin }
method Stdout (line 101) | func (f *builtinFn) Stdout() io.Writer { return f.stdout }
method Stderr (line 102) | func (f *builtinFn) Stderr() io.Writer { return f.stderr }
function NewBuiltinFn (line 31) | func NewBuiltinFn(
FILE: internal/sh/builtin/append.go
type appendFn (line 11) | type appendFn struct
method ArgNames (line 21) | func (appendfn *appendFn) ArgNames() []sh.FnArg {
method Run (line 28) | func (appendfn *appendFn) Run(in io.Reader, out io.Writer, err io.Writ...
method SetArgs (line 33) | func (appendfn *appendFn) SetArgs(args []sh.Obj) error {
function newAppend (line 17) | func newAppend() *appendFn {
FILE: internal/sh/builtin/append_test.go
type testcase (line 10) | type testcase struct
function testAppend (line 18) | func testAppend(t *testing.T, tc testcase) {
function TestAppend (line 50) | func TestAppend(t *testing.T) {
FILE: internal/sh/builtin/chdir.go
type chdirFn (line 13) | type chdirFn struct
method ArgNames (line 22) | func (chdir *chdirFn) ArgNames() []sh.FnArg {
method Run (line 28) | func (chdir *chdirFn) Run(in io.Reader, out io.Writer, ioerr io.Writer...
method SetArgs (line 36) | func (chdir *chdirFn) SetArgs(args []sh.Obj) error {
function newChdir (line 18) | func newChdir() *chdirFn {
FILE: internal/sh/builtin/exec_test.go
function execSuccess (line 9) | func execSuccess(t *testing.T, scriptContents string) string {
function execFailure (line 21) | func execFailure(t *testing.T, scriptContents string) {
FILE: internal/sh/builtin/exit.go
type exitFn (line 13) | type exitFn struct
method ArgNames (line 22) | func (e *exitFn) ArgNames() []sh.FnArg {
method Run (line 28) | func (e *exitFn) Run(in io.Reader, out io.Writer, err io.Writer) ([]sh...
method SetArgs (line 33) | func (e *exitFn) SetArgs(args []sh.Obj) error {
function newExit (line 18) | func newExit() Fn {
FILE: internal/sh/builtin/exit_test.go
function TestExit (line 9) | func TestExit(t *testing.T) {
FILE: internal/sh/builtin/format.go
type formatFn (line 12) | type formatFn struct
method ArgNames (line 22) | func (f *formatFn) ArgNames() []sh.FnArg {
method Run (line 29) | func (f *formatFn) Run(in io.Reader, out io.Writer, err io.Writer) ([]...
method SetArgs (line 33) | func (f *formatFn) SetArgs(args []sh.Obj) error {
function newFormat (line 18) | func newFormat() *formatFn {
FILE: internal/sh/builtin/format_test.go
function TestFormat (line 5) | func TestFormat(t *testing.T) {
function TestFormatfErrors (line 118) | func TestFormatfErrors(t *testing.T) {
FILE: internal/sh/builtin/glob.go
type globFn (line 13) | type globFn struct
method ArgNames (line 22) | func (p *globFn) ArgNames() []sh.FnArg {
method Run (line 26) | func (g *globFn) Run(in io.Reader, out io.Writer, e io.Writer) ([]sh.O...
method SetArgs (line 41) | func (g *globFn) SetArgs(args []sh.Obj) error {
function newGlob (line 18) | func newGlob() *globFn {
FILE: internal/sh/builtin/glob_test.go
function setup (line 11) | func setup(t *testing.T) (string, func()) {
function createFile (line 22) | func createFile(t *testing.T, path string) {
function TestGlobNoResult (line 31) | func TestGlobNoResult(t *testing.T) {
function TestGlobOneResult (line 49) | func TestGlobOneResult(t *testing.T) {
function TestGlobMultipleResults (line 68) | func TestGlobMultipleResults(t *testing.T) {
function TestGlobInvalidPatternError (line 108) | func TestGlobInvalidPatternError(t *testing.T) {
function TestFatalFailure (line 122) | func TestFatalFailure(t *testing.T) {
FILE: internal/sh/builtin/len.go
type lenFn (line 12) | type lenFn struct
method ArgNames (line 21) | func (l *lenFn) ArgNames() []sh.FnArg {
method Run (line 31) | func (l *lenFn) Run(in io.Reader, out io.Writer, err io.Writer) ([]sh....
method SetArgs (line 35) | func (l *lenFn) SetArgs(args []sh.Obj) error {
function newLen (line 17) | func newLen() *lenFn {
function lenresult (line 27) | func lenresult(res int) []sh.Obj {
FILE: internal/sh/builtin/len_test.go
function TestLen (line 10) | func TestLen(t *testing.T) {
FILE: internal/sh/builtin/loader.go
type Fn (line 12) | type Fn interface
type Constructor (line 22) | type Constructor
function Constructors (line 26) | func Constructors() map[string]Constructor {
FILE: internal/sh/builtin/print.go
type printFn (line 12) | type printFn struct
method ArgNames (line 22) | func (p *printFn) ArgNames() []sh.FnArg {
method Run (line 29) | func (p *printFn) Run(in io.Reader, out io.Writer, err io.Writer) ([]s...
method SetArgs (line 34) | func (p *printFn) SetArgs(args []sh.Obj) error {
function newPrint (line 18) | func newPrint() *printFn {
FILE: internal/sh/builtin/print_test.go
function TestPrint (line 5) | func TestPrint(t *testing.T) {
function TestPrintErrors (line 85) | func TestPrintErrors(t *testing.T) {
FILE: internal/sh/builtin/split.go
type splitFn (line 12) | type splitFn struct
method ArgNames (line 22) | func (s *splitFn) ArgNames() []sh.FnArg {
method Run (line 29) | func (s *splitFn) Run(in io.Reader, out io.Writer, err io.Writer) ([]s...
method SetArgs (line 56) | func (s *splitFn) SetArgs(args []sh.Obj) error {
function newSplit (line 18) | func newSplit() *splitFn {
function splitByList (line 71) | func splitByList(content string, delims []sh.Obj) []string {
function splitByFn (line 89) | func splitByFn(content string, splitFunc sh.FnDef) []string {
FILE: internal/sh/builtin/split_test.go
function TestSplit (line 10) | func TestSplit(t *testing.T) {
function TestSplitingByFuncWrongWontPanic (line 65) | func TestSplitingByFuncWrongWontPanic(t *testing.T) {
FILE: internal/sh/cmd.go
type Cmd (line 17) | type Cmd struct
method Stdin (line 64) | func (c *Cmd) Stdin() io.Reader { return c.Cmd.Stdin }
method Stdout (line 65) | func (c *Cmd) Stdout() io.Writer { return c.Cmd.Stdout }
method Stderr (line 66) | func (c *Cmd) Stderr() io.Writer { return c.Cmd.Stderr }
method SetStdin (line 68) | func (c *Cmd) SetStdin(in io.Reader) { c.Cmd.Stdin = in }
method SetStdout (line 69) | func (c *Cmd) SetStdout(out io.Writer) { c.Cmd.Stdout = out }
method SetStderr (line 70) | func (c *Cmd) SetStderr(err io.Writer) { c.Cmd.Stderr = err }
method SetArgs (line 72) | func (c *Cmd) SetArgs(nodeArgs []sh.Obj) error {
method Args (line 103) | func (c *Cmd) Args() []ast.Expr { return c.argExprs }
method SetEnviron (line 105) | func (c *Cmd) SetEnviron(env []string) {
method Wait (line 109) | func (c *Cmd) Wait() error {
method Start (line 119) | func (c *Cmd) Start() error {
method Results (line 127) | func (c *Cmd) Results() []sh.Obj { return nil }
type errCmdNotFound (line 24) | type errCmdNotFound struct
method NotFound (line 37) | func (e *errCmdNotFound) NotFound() bool {
function newCmdNotFound (line 29) | func newCmdNotFound(format string, arg ...interface{}) error {
function NewCmd (line 41) | func NewCmd(name string) (*Cmd, error) {
FILE: internal/sh/fncall.go
type FnArg (line 14) | type FnArg struct
type UserFn (line 19) | type UserFn struct
method ArgNames (line 62) | func (fn *UserFn) ArgNames() []sh.FnArg { return fn.argNames }
method AddArgName (line 64) | func (fn *UserFn) AddArgName(arg sh.FnArg) {
method SetArgs (line 68) | func (fn *UserFn) SetArgs(args []sh.Obj) error {
method Name (line 140) | func (fn *UserFn) Name() string { return fn.name }
method SetRepr (line 142) | func (fn *UserFn) SetRepr(repr string) {
method closeDescriptors (line 146) | func (fn *UserFn) closeDescriptors(closers []io.Closer) {
method execute (line 152) | func (fn *UserFn) execute() ([]sh.Obj, error) {
method Start (line 160) | func (fn *UserFn) Start() error {
method Results (line 170) | func (fn *UserFn) Results() []sh.Obj { return fn.results }
method Wait (line 172) | func (fn *UserFn) Wait() error {
method SetEnviron (line 180) | func (fn *UserFn) SetEnviron(env []string) {
method SetStderr (line 184) | func (fn *UserFn) SetStderr(w io.Writer) {
method SetStdout (line 188) | func (fn *UserFn) SetStdout(w io.Writer) {
method SetStdin (line 192) | func (fn *UserFn) SetStdin(r io.Reader) {
method Stdin (line 196) | func (fn *UserFn) Stdin() io.Reader { return fn.stdin }
method Stdout (line 197) | func (fn *UserFn) Stdout() io.Writer { return fn.stdout }
method Stderr (line 198) | func (fn *UserFn) Stderr() io.Writer { return fn.stderr }
method String (line 200) | func (fn *UserFn) String() string {
method StdoutPipe (line 207) | func (fn *UserFn) StdoutPipe() (io.ReadCloser, error) {
function NewUserFn (line 39) | func NewUserFn(name string, args []sh.FnArg, body *ast.Tree, parent *She...
FILE: internal/sh/fndef.go
type fnDef (line 13) | type fnDef struct
method Name (line 59) | func (fnDef *fnDef) Name() string { return fnDef.name }
method ArgNames (line 60) | func (fnDef *fnDef) ArgNames() []sh.FnArg { return fnDef.argNames }
method Environ (line 61) | func (fnDef *fnDef) Environ() []string { return fnDef.environ }
method SetEnviron (line 63) | func (fnDef *fnDef) SetEnviron(env []string) {
method SetStdin (line 67) | func (fnDef *fnDef) SetStdin(r io.Reader) {
method SetStderr (line 71) | func (fnDef *fnDef) SetStderr(w io.Writer) {
method SetStdout (line 75) | func (fnDef *fnDef) SetStdout(w io.Writer) {
method Stdin (line 79) | func (fnDef *fnDef) Stdin() io.Reader { return fnDef.stdin }
method Stdout (line 80) | func (fnDef *fnDef) Stdout() io.Writer { return fnDef.stdout }
method Stderr (line 81) | func (fnDef *fnDef) Stderr() io.Writer { return fnDef.stderr }
type userFnDef (line 24) | type userFnDef struct
method Build (line 94) | func (ufnDef *userFnDef) Build() sh.Fn {
type builtinFnDef (line 28) | type builtinFnDef struct
method Build (line 115) | func (bfnDef *builtinFnDef) Build() sh.Fn {
function newFnDef (line 35) | func newFnDef(name string, parent *Shell, args []*ast.FnArgNode, body *a...
function newUserFnDef (line 83) | func newUserFnDef(name string, parent *Shell, args []*ast.FnArgNode, bod...
function newBuiltinFnDef (line 103) | func newBuiltinFnDef(name string, parent *Shell, constructor builtin.Con...
FILE: internal/sh/functions_test.go
function TestFunctionsClosures (line 5) | func TestFunctionsClosures(t *testing.T) {
function TestFunctionsVariables (line 105) | func TestFunctionsVariables(t *testing.T) {
function TestFunctionsStateless (line 128) | func TestFunctionsStateless(t *testing.T) {
FILE: internal/sh/internal/fixture/fixture.go
type NashDirs (line 12) | type NashDirs struct
function SetupShell (line 24) | func SetupShell(t *testing.T) (*nash.Shell, func()) {
function SetupNashDirs (line 37) | func SetupNashDirs(t *testing.T) NashDirs {
FILE: internal/sh/ioutils_test.go
function writeFile (line 9) | func writeFile(t *testing.T, filename string, data string) {
function chdir (line 16) | func chdir(t *testing.T, dir string) {
function getwd (line 25) | func getwd(t *testing.T) string {
FILE: internal/sh/log.go
type LogFn (line 9) | type LogFn
function NewLog (line 12) | func NewLog(ns string, enable bool) LogFn {
FILE: internal/sh/rfork.go
method executeRfork (line 11) | func (sh *Shell) executeRfork(rfork *ast.RforkNode) error {
FILE: internal/sh/rfork_linux.go
function getProcAttrs (line 19) | func getProcAttrs(flags uintptr) *syscall.SysProcAttr {
function dialRc (line 48) | func dialRc(sockpath string) (net.Conn, error) {
method executeRfork (line 68) | func (sh *Shell) executeRfork(rfork *ast.RforkNode) error {
function getflags (line 224) | func getflags(flags string) (uintptr, error) {
FILE: internal/sh/rfork_linux_test.go
function getletters (line 12) | func getletters() string {
function getvalid (line 24) | func getvalid() string {
function testTblFlagsOK (line 28) | func testTblFlagsOK(flagstr string, expected uintptr, t *testing.T) {
function TestRforkFlags (line 42) | func TestRforkFlags(t *testing.T) {
FILE: internal/sh/rfork_plan9.go
method executeRfork (line 10) | func (sh *Shell) executeRfork(rfork *RforkNode) error {
function getflags (line 15) | func getflags(flags string) (uintptr, error) {
FILE: internal/sh/shell.go
constant logNS (line 27) | logNS = "nashell.Shell"
constant defPrompt (line 28) | defPrompt = "\033[31mλ>\033[0m "
type Env (line 33) | type Env
type Var (line 34) | type Var
type Fns (line 35) | type Fns
type StatusCode (line 37) | type StatusCode
type Shell (line 40) | type Shell struct
method NashPath (line 194) | func (shell *Shell) NashPath() string {
method initEnv (line 199) | func (shell *Shell) initEnv(processEnv []string) error {
method Reset (line 254) | func (shell *Shell) Reset() {
method SetDebug (line 261) | func (shell *Shell) SetDebug(d bool) {
method SetInteractive (line 267) | func (shell *Shell) SetInteractive(i bool) {
method Interactive (line 275) | func (shell *Shell) Interactive() bool {
method SetName (line 283) | func (shell *Shell) SetName(a string) {
method Name (line 287) | func (shell *Shell) Name() string { return shell.name }
method SetParent (line 289) | func (shell *Shell) SetParent(a *Shell) {
method Environ (line 293) | func (shell *Shell) Environ() Env {
method Getenv (line 301) | func (shell *Shell) Getenv(name string) (sh.Obj, bool) {
method Setenv (line 310) | func (shell *Shell) Setenv(name string, value sh.Obj) {
method SetEnviron (line 322) | func (shell *Shell) SetEnviron(processEnv []string) {
method GetLocalvar (line 339) | func (shell *Shell) GetLocalvar(name string) (sh.Obj, bool) {
method Getvar (line 346) | func (shell *Shell) Getvar(name string) (sh.Obj, bool) {
method GetFn (line 359) | func (shell *Shell) GetFn(name string) (*sh.FnObj, error) {
method Setbindfn (line 376) | func (shell *Shell) Setbindfn(name string, value sh.FnDef) {
method Getbindfn (line 380) | func (shell *Shell) Getbindfn(cmdName string) (sh.FnDef, bool) {
method Newvar (line 393) | func (shell *Shell) Newvar(name string, value sh.Obj) {
method Setvar (line 400) | func (shell *Shell) Setvar(name string, value sh.Obj) bool {
method IsFn (line 414) | func (shell *Shell) IsFn() bool { return shell.isFn }
method SetIsFn (line 415) | func (shell *Shell) SetIsFn(b bool) { shell.isFn = b }
method SetNashdPath (line 418) | func (shell *Shell) SetNashdPath(path string) {
method SetStdin (line 423) | func (shell *Shell) SetStdin(in io.Reader) {
method SetStdout (line 428) | func (shell *Shell) SetStdout(out io.Writer) {
method SetStderr (line 433) | func (shell *Shell) SetStderr(err io.Writer) {
method Stdout (line 437) | func (shell *Shell) Stdout() io.Writer { return shell.stdout }
method Stderr (line 438) | func (shell *Shell) Stderr() io.Writer { return shell.stderr }
method Stdin (line 439) | func (shell *Shell) Stdin() io.Reader { return shell.stdin }
method SetTree (line 443) | func (shell *Shell) SetTree(t *ast.Tree) {
method Tree (line 448) | func (shell *Shell) Tree() *ast.Tree { return shell.root }
method SetRepr (line 451) | func (shell *Shell) SetRepr(a string) {
method setupBuiltin (line 455) | func (shell *Shell) setupBuiltin() {
method setupDefaultBindings (line 462) | func (shell *Shell) setupDefaultBindings() error {
method setup (line 485) | func (shell *Shell) setup() error {
method setupSignals (line 506) | func (shell *Shell) setupSignals() {
method TriggerCTRLC (line 532) | func (shell *Shell) TriggerCTRLC() error {
method setIntr (line 542) | func (shell *Shell) setIntr(b bool) {
method getIntr (line 552) | func (shell *Shell) getIntr() bool {
method Exec (line 561) | func (shell *Shell) Exec(path, content string) error {
method ExecFile (line 575) | func (shell *Shell) ExecFile(path string) error {
method newvar (line 593) | func (shell *Shell) newvar(name *ast.NameNode, value sh.Obj) error {
method setvar (line 631) | func (shell *Shell) setvar(name *ast.NameNode, value sh.Obj) error {
method setvars (line 675) | func (shell *Shell) setvars(names []*ast.NameNode, values []sh.Obj) er...
method newvars (line 685) | func (shell *Shell) newvars(names []*ast.NameNode, values []sh.Obj) {
method setcmdvars (line 691) | func (shell *Shell) setcmdvars(names []*ast.NameNode, stdout, stderr, ...
method newcmdvars (line 719) | func (shell *Shell) newcmdvars(names []*ast.NameNode, stdout, stderr, ...
method evalConcat (line 738) | func (shell *Shell) evalConcat(path ast.Expr) (string, error) {
method executeNode (line 812) | func (shell *Shell) executeNode(node ast.Node) ([]sh.Obj, error) {
method ExecuteTree (line 869) | func (shell *Shell) ExecuteTree(tr *ast.Tree) ([]sh.Obj, error) {
method executeTree (line 874) | func (shell *Shell) executeTree(tr *ast.Tree, stopable bool) ([]sh.Obj...
method executeReturn (line 917) | func (shell *Shell) executeReturn(n *ast.ReturnNode) ([]sh.Obj, error) {
method getNashRootFromGOPATH (line 936) | func (shell *Shell) getNashRootFromGOPATH(preverr error) (string, erro...
method executeImport (line 950) | func (shell *Shell) executeImport(node *ast.ImportNode) error {
method executePipe (line 1028) | func (shell *Shell) executePipe(pipe *ast.PipeNode) (sh.Obj, error) {
method openRedirectLocation (line 1214) | func (shell *Shell) openRedirectLocation(location ast.Expr) (io.WriteC...
method setRedirects (line 1266) | func (shell *Shell) setRedirects(cmd sh.Runner, redirDecls []*ast.Redi...
method buildRedirect (line 1281) | func (shell *Shell) buildRedirect(cmd sh.Runner, redirDecl *ast.Redire...
method newBindfnRunner (line 1372) | func (shell *Shell) newBindfnRunner(
method getCommand (line 1392) | func (shell *Shell) getCommand(c *ast.CommandNode) (sh.Runner, bool, e...
method executeCommand (line 1443) | func (shell *Shell) executeCommand(c *ast.CommandNode) (sh.Obj, error) {
method evalList (line 1507) | func (shell *Shell) evalList(argList *ast.ListExpr) (sh.Obj, error) {
method evalArgList (line 1522) | func (shell *Shell) evalArgList(argList *ast.ListExpr) ([]sh.Obj, erro...
method evalIndex (line 1541) | func (shell *Shell) evalIndex(index ast.Expr) (int, error) {
method evalIndexedVar (line 1572) | func (shell *Shell) evalIndexedVar(indexVar *ast.IndexExpr) (sh.Obj, e...
method evalArgIndexedVar (line 1596) | func (shell *Shell) evalArgIndexedVar(indexVar *ast.IndexExpr) ([]sh.O...
method evalVariable (line 1628) | func (shell *Shell) evalVariable(a ast.Expr) (sh.Obj, error) {
method evalArgVariable (line 1653) | func (shell *Shell) evalArgVariable(a ast.Expr) ([]sh.Obj, error) {
method evalExprs (line 1688) | func (shell *Shell) evalExprs(exprs []ast.Expr) ([]sh.Obj, error) {
method evalArgExprs (line 1703) | func (shell *Shell) evalArgExprs(exprs []ast.Expr) ([]sh.Obj, error) {
method evalArgExpr (line 1718) | func (shell *Shell) evalArgExpr(expr ast.Expr) ([]sh.Obj, error) {
method evalExpr (line 1776) | func (shell *Shell) evalExpr(expr ast.Expr) (sh.Obj, error) {
method executeSetenvAssign (line 1830) | func (shell *Shell) executeSetenvAssign(assign *ast.AssignNode) error {
method executeSetenvExec (line 1851) | func (shell *Shell) executeSetenvExec(assign *ast.ExecAssignNode) error {
method executeSetenv (line 1871) | func (shell *Shell) executeSetenv(v *ast.SetenvNode) error {
method concatElements (line 1901) | func (shell *Shell) concatElements(expr *ast.ConcatExpr) (string, erro...
method execCmdOutput (line 1926) | func (shell *Shell) execCmdOutput(cmd ast.Node,
method executeExecAssignCmd (line 1976) | func (shell *Shell) executeExecAssignCmd(v ast.Node) (stdout, stderr, ...
method executeExecAssignFn (line 1991) | func (shell *Shell) executeExecAssignFn(assign *ast.ExecAssignNode) ([...
method executeExecAssign (line 2018) | func (shell *Shell) executeExecAssign(v *ast.ExecAssignNode) (err erro...
method initVar (line 2045) | func (shell *Shell) initVar(name *ast.NameNode, value ast.Expr) error {
method executeVarAssign (line 2053) | func (shell *Shell) executeVarAssign(v *ast.VarAssignDeclNode) error {
method executeVarExecAssign (line 2075) | func (shell *Shell) executeVarExecAssign(v *ast.VarExecAssignDeclNode)...
method executeAssignment (line 2103) | func (shell *Shell) executeAssignment(v *ast.AssignNode) error {
method evalIfArgument (line 2129) | func (shell *Shell) evalIfArgument(arg ast.Node) (sh.Obj, error) {
method evalIfArguments (line 2146) | func (shell *Shell) evalIfArguments(n *ast.IfNode) (string, string, er...
method executeIfEqual (line 2180) | func (shell *Shell) executeIfEqual(n *ast.IfNode) ([]sh.Obj, error) {
method executeIfNotEqual (line 2196) | func (shell *Shell) executeIfNotEqual(n *ast.IfNode) ([]sh.Obj, error) {
method executeFnInv (line 2212) | func (shell *Shell) executeFnInv(n *ast.FnInvNode) ([]sh.Obj, error) {
method executeInfLoop (line 2271) | func (shell *Shell) executeInfLoop(tr *ast.Tree) ([]sh.Obj, error) {
method executeFor (line 2322) | func (shell *Shell) executeFor(n *ast.ForNode) ([]sh.Obj, error) {
method executeFnDecl (line 2428) | func (shell *Shell) executeFnDecl(n *ast.FnDeclNode) error {
method executeBindFn (line 2439) | func (shell *Shell) executeBindFn(n *ast.BindFnNode) error {
method executeIf (line 2455) | func (shell *Shell) executeIf(n *ast.IfNode) ([]sh.Obj, error) {
type errIgnore (line 73) | type errIgnore struct
method Ignore (line 100) | func (e *errIgnore) Ignore() bool { return true }
type errInterrupted (line 77) | type errInterrupted struct
method Interrupted (line 108) | func (e *errInterrupted) Interrupted() bool { return true }
type errStopWalking (line 81) | type errStopWalking struct
method StopWalking (line 116) | func (e *errStopWalking) StopWalking() bool { return true }
constant ESuccess (line 87) | ESuccess StatusCode = 0
constant ENotFound (line 88) | ENotFound = 127
constant ENotStarted (line 89) | ENotStarted = 255
function newErrIgnore (line 92) | func newErrIgnore(format string, arg ...interface{}) error {
function newErrInterrupted (line 102) | func newErrInterrupted(format string, arg ...interface{}) error {
function newErrStopWalking (line 110) | func newErrStopWalking() *errStopWalking {
function NewAbortShell (line 118) | func NewAbortShell(nashpath string, nashroot string) (*Shell, error) {
function NewShell (line 125) | func NewShell(nashpath string, nashroot string) (*Shell, error) {
function newShell (line 129) | func newShell(nashpath string, nashroot string, abort bool) (*Shell, err...
function NewSubShell (line 176) | func NewSubShell(name string, parent *Shell) *Shell {
function isValidNashRoot (line 945) | func isValidNashRoot(nashroot string) bool {
function validateDirs (line 2467) | func validateDirs(nashpath string, nashroot string) error {
function validateDir (line 2482) | func validateDir(dir string) error {
FILE: internal/sh/shell_import_test.go
function TestImportsLibFromNashPathLibDir (line 13) | func TestImportsLibFromNashPathLibDir(t *testing.T) {
function TestImportsLibFromNashPathLibDirBeforeNashRootStdlib (line 30) | func TestImportsLibFromNashPathLibDirBeforeNashRootStdlib(t *testing.T) {
function TestImportsLibFromNashRootStdlib (line 53) | func TestImportsLibFromNashRootStdlib(t *testing.T) {
function TestImportsLibFromWorkingDirBeforeLibAndStdlib (line 70) | func TestImportsLibFromWorkingDirBeforeLibAndStdlib(t *testing.T) {
function TestStdErrOnInvalidSearchPaths (line 106) | func TestStdErrOnInvalidSearchPaths(t *testing.T) {
type testshell (line 197) | type testshell struct
method ExecCheckingOutput (line 202) | func (s *testshell) ExecCheckingOutput(t *testing.T, code string, expe...
function newTestShell (line 220) | func newTestShell(t *testing.T, nashpath string, nashroot string) *tests...
FILE: internal/sh/shell_linux_test.go
function init (line 19) | func init() {
function TestExecuteRforkUserNS (line 68) | func TestExecuteRforkUserNS(t *testing.T) {
function TestExecuteRforkEnvVars (line 94) | func TestExecuteRforkEnvVars(t *testing.T) {
function TestExecuteRforkUserNSNested (line 117) | func TestExecuteRforkUserNSNested(t *testing.T) {
FILE: internal/sh/shell_regression_test.go
function TestExecuteIssue68 (line 12) | func TestExecuteIssue68(t *testing.T) {
function TestExecuteErrorSuppression (line 45) | func TestExecuteErrorSuppression(t *testing.T) {
FILE: internal/sh/shell_test.go
type execTestCase (line 26) | type execTestCase struct
type testFixture (line 35) | type testFixture struct
function TestInitEnv (line 44) | func TestInitEnv(t *testing.T) {
function TestExecuteFile (line 61) | func TestExecuteFile(t *testing.T) {
function TestExecuteCommand (line 90) | func TestExecuteCommand(t *testing.T) {
function TestExecuteAssignment (line 142) | func TestExecuteAssignment(t *testing.T) {
function TestExecuteMultipleAssignment (line 208) | func TestExecuteMultipleAssignment(t *testing.T) {
function TestExecuteCmdAssignment (line 273) | func TestExecuteCmdAssignment(t *testing.T) {
function TestExecuteCmdMultipleAssignment (line 330) | func TestExecuteCmdMultipleAssignment(t *testing.T) {
function TestExecuteRedirection (line 445) | func TestExecuteRedirection(t *testing.T) {
function TestExecuteRedirectionMap (line 532) | func TestExecuteRedirectionMap(t *testing.T) {
function TestExecuteSetenv (line 564) | func TestExecuteSetenv(t *testing.T) {
function TestExecuteCd (line 606) | func TestExecuteCd(t *testing.T) {
function TestExecuteImport (line 666) | func TestExecuteImport(t *testing.T) {
function TestExecuteIfEqual (line 701) | func TestExecuteIfEqual(t *testing.T) {
function TestExecuteIfElse (line 798) | func TestExecuteIfElse(t *testing.T) {
function TestExecuteIfElseIf (line 841) | func TestExecuteIfElseIf(t *testing.T) {
function TestExecuteFnDecl (line 885) | func TestExecuteFnDecl(t *testing.T) {
function TestExecuteFnInv (line 899) | func TestExecuteFnInv(t *testing.T) {
function TestFnComposition (line 1011) | func TestFnComposition(t *testing.T) {
function TestExecuteFnInvOthers (line 1041) | func TestExecuteFnInvOthers(t *testing.T) {
function TestNonInteractive (line 1073) | func TestNonInteractive(t *testing.T) {
function TestExecuteBindFn (line 1126) | func TestExecuteBindFn(t *testing.T) {
function TestExecutePipe (line 1188) | func TestExecutePipe(t *testing.T) {
function TestExecuteRedirectionPipe (line 1238) | func TestExecuteRedirectionPipe(t *testing.T) {
function testTCPRedirection (line 1257) | func testTCPRedirection(t *testing.T, port, command string) {
function TestTCPRedirection (line 1302) | func TestTCPRedirection(t *testing.T) {
function TestExecuteUnixRedirection (line 1307) | func TestExecuteUnixRedirection(t *testing.T) {
function TestExecuteUDPRedirection (line 1385) | func TestExecuteUDPRedirection(t *testing.T) {
function TestExecuteReturn (line 1447) | func TestExecuteReturn(t *testing.T) {
function TestExecuteFnAsFirstClass (line 1532) | func TestExecuteFnAsFirstClass(t *testing.T) {
function TestExecuteConcat (line 1565) | func TestExecuteConcat(t *testing.T) {
function TestExecuteFor (line 1605) | func TestExecuteFor(t *testing.T) {
function TestExecuteInfiniteLoop (line 1636) | func TestExecuteInfiniteLoop(t *testing.T) {
function TestExecuteVariableIndexing (line 1691) | func TestExecuteVariableIndexing(t *testing.T) {
function TestExecuteSubShellDoesNotOverwriteparentEnv (line 1778) | func TestExecuteSubShellDoesNotOverwriteparentEnv(t *testing.T) {
function TestExecuteInterruptDoesNotCancelLoop (line 1811) | func TestExecuteInterruptDoesNotCancelLoop(t *testing.T) {
function TestExecuteErrorSuppressionAll (line 1829) | func TestExecuteErrorSuppressionAll(t *testing.T) {
function TestExecuteGracefullyError (line 1875) | func TestExecuteGracefullyError(t *testing.T) {
function TestExecuteMultilineCmd (line 1907) | func TestExecuteMultilineCmd(t *testing.T) {
function TestExecuteMultilineCmdAssign (line 1950) | func TestExecuteMultilineCmdAssign(t *testing.T) {
function TestExecuteMultiReturnUnfinished (line 1996) | func TestExecuteMultiReturnUnfinished(t *testing.T) {
function TestExecuteVariadicFn (line 2046) | func TestExecuteVariadicFn(t *testing.T) {
function setup (line 2134) | func setup(t *testing.T) (testFixture, func()) {
function testExecuteFile (line 2153) | func testExecuteFile(t *testing.T, path, expected string, before string) {
function testShellExec (line 2175) | func testShellExec(t *testing.T, shell *sh.Shell, testcase execTestCase) {
function testExec (line 2221) | func testExec(t *testing.T, testcase execTestCase) {
function testInteractiveExec (line 2229) | func testInteractiveExec(t *testing.T, testcase execTestCase) {
FILE: internal/sh/shell_var_test.go
function TestVarAssign (line 10) | func TestVarAssign(t *testing.T) {
function TestVarExecAssign (line 47) | func TestVarExecAssign(t *testing.T) {
FILE: internal/sh/util.go
function init (line 18) | func init() {
function randRunes (line 22) | func randRunes(n int) string {
function buildenv (line 30) | func buildenv(e Env) []string {
function printVar (line 55) | func printVar(out io.Writer, name string, val sh.Obj) {
function printEnv (line 65) | func printEnv(out io.Writer, name string) {
function getErrStatus (line 69) | func getErrStatus(err error, def string) string {
function nashdAutoDiscover (line 81) | func nashdAutoDiscover() string {
FILE: internal/sh/util_test.go
function TestBuildEnv (line 10) | func TestBuildEnv(t *testing.T) {
FILE: internal/testing/fixture/io.go
function Tmpdir (line 15) | func Tmpdir(t *testing.T) (string, func()) {
function MkdirAll (line 38) | func MkdirAll(t *testing.T, nashlib string) {
function CreateFiles (line 53) | func CreateFiles(t *testing.T, filepaths []string) map[string]string {
function CreateFile (line 74) | func CreateFile(t *testing.T, f string) string {
function WorkingDir (line 90) | func WorkingDir(t *testing.T) string {
function ChangeDir (line 100) | func ChangeDir(t *testing.T, path string) {
function Chmod (line 109) | func Chmod(t *testing.T, path string, mode os.FileMode) {
FILE: nash.go
type Shell (line 17) | type Shell struct
method SetDebug (line 52) | func (nash *Shell) SetDebug(b bool) {
method SetInteractive (line 57) | func (nash *Shell) SetInteractive(b bool) {
method NashPath (line 61) | func (nash *Shell) NashPath() string {
method Environ (line 66) | func (nash *Shell) Environ() shell.Env {
method GetFn (line 71) | func (nash *Shell) GetFn(name string) (sh.FnDef, error) {
method Prompt (line 80) | func (nash *Shell) Prompt() string {
method SetNashdPath (line 91) | func (nash *Shell) SetNashdPath(path string) {
method Exec (line 101) | func (nash *Shell) Exec(path, content string) error {
method ExecOutput (line 114) | func (nash *Shell) ExecOutput(path, content string) ([]byte, error) {
method ExecuteString (line 127) | func (nash *Shell) ExecuteString(path, content string) error {
method ExecFile (line 133) | func (nash *Shell) ExecFile(path string, args ...string) error {
method ExecuteFile (line 145) | func (nash *Shell) ExecuteFile(path string) error {
method ExecuteTree (line 151) | func (nash *Shell) ExecuteTree(tr *ast.Tree) ([]sh.Obj, error) {
method ExecTree (line 157) | func (nash *Shell) ExecTree(tree *ast.Tree) ([]sh.Obj, error) {
method SetStdout (line 162) | func (nash *Shell) SetStdout(out io.Writer) {
method SetStderr (line 167) | func (nash *Shell) SetStderr(err io.Writer) {
method SetStdin (line 172) | func (nash *Shell) SetStdin(in io.Reader) {
method Stdin (line 177) | func (nash *Shell) Stdin() io.Reader { return nash.interp.Stdin() }
method Stdout (line 180) | func (nash *Shell) Stdout() io.Writer { return nash.interp.Stdout() }
method Stderr (line 183) | func (nash *Shell) Stderr() io.Writer { return nash.interp.Stderr() }
method Setvar (line 187) | func (nash *Shell) Setvar(name string, value sh.Obj) bool {
method Newvar (line 192) | func (nash *Shell) Newvar(name string, value sh.Obj) {
method Getvar (line 197) | func (nash *Shell) Getvar(name string) (sh.Obj, bool) {
function newShell (line 22) | func newShell(nashpath string, nashroot string, abort bool) (*Shell, err...
function New (line 41) | func New(nashpath string, nashroot string) (*Shell, error) {
function NewAbort (line 47) | func NewAbort(nashpath string, nashroot string) (*Shell, error) {
function args2Nash (line 201) | func args2Nash(args []string) string {
FILE: nash_test.go
function TestExecuteFile (line 16) | func TestExecuteFile(t *testing.T) {
function TestExecuteString (line 40) | func TestExecuteString(t *testing.T) {
function TestSetvar (line 78) | func TestSetvar(t *testing.T) {
function newTestShell (line 107) | func newTestShell(t *testing.T) (*Shell, func()) {
function tmpdir (line 124) | func tmpdir(t *testing.T) (string, func()) {
FILE: parser/parse.go
type Parser (line 17) | type Parser struct
method Parse (line 65) | func (p *Parser) Parse() (tr *ast.Tree, err error) {
method next (line 92) | func (p *Parser) next() scanner.Token {
method backup (line 109) | func (p *Parser) backup(it scanner.Token) error {
method ignore (line 120) | func (p *Parser) ignore() {
method peek (line 129) | func (p *Parser) peek() scanner.Token {
method parseBlock (line 135) | func (p *Parser) parseBlock(lineStart, columnStart int) (*ast.BlockNod...
method parseStatement (line 178) | func (p *Parser) parseStatement() (ast.Node, error) {
method parseIndexing (line 215) | func (p *Parser) parseIndexing() (ast.Expr, error) {
method parseVariable (line 255) | func (p *Parser) parseVariable(tok *scanner.Token, allowVararg bool) (...
method parsePipe (line 306) | func (p *Parser) parsePipe(first *ast.CommandNode) (ast.Node, error) {
method parseCommand (line 357) | func (p *Parser) parseCommand(it scanner.Token) (ast.Node, error) {
method parseRedirection (line 465) | func (p *Parser) parseRedirection(it scanner.Token) (*ast.RedirectNode...
method parseImport (line 562) | func (p *Parser) parseImport(importToken scanner.Token) (ast.Node, err...
method parseSetenv (line 586) | func (p *Parser) parseSetenv(it scanner.Token) (ast.Node, error) {
method getArgument (line 629) | func (p *Parser) getArgument(tok *scanner.Token, cfg exprConfig) (ast....
method getConcatArg (line 690) | func (p *Parser) getConcatArg(firstArg ast.Expr) (ast.Expr, error) {
method parseAssignment (line 721) | func (p *Parser) parseAssignment(ident scanner.Token) (ast.Node, error) {
method parseList (line 794) | func (p *Parser) parseList(tok *scanner.Token) (ast.Node, error) {
method parseAssignValues (line 854) | func (p *Parser) parseAssignValues(names []*ast.NameNode) (ast.Node, e...
method parseAssignCmdOut (line 907) | func (p *Parser) parseAssignCmdOut(identifiers []*ast.NameNode) (ast.N...
method parseRfork (line 949) | func (p *Parser) parseRfork(it scanner.Token) (ast.Node, error) {
method parseIfExpr (line 988) | func (p *Parser) parseIfExpr() (ast.Node, error) {
method parseIf (line 1003) | func (p *Parser) parseIf(it scanner.Token) (ast.Node, error) {
method parseFnArgs (line 1073) | func (p *Parser) parseFnArgs() ([]*ast.FnArgNode, error) {
method parseVar (line 1119) | func (p *Parser) parseVar(it scanner.Token) (ast.Node, error) {
method parseFnDecl (line 1163) | func (p *Parser) parseFnDecl(it scanner.Token) (ast.Node, error) {
method parseFnInv (line 1208) | func (p *Parser) parseFnInv(ident scanner.Token, allowSemicolon bool) ...
method parseElse (line 1272) | func (p *Parser) parseElse() (*ast.BlockNode, bool, error) {
method parseBindFn (line 1303) | func (p *Parser) parseBindFn(bindIt scanner.Token) (ast.Node, error) {
method parseReturn (line 1325) | func (p *Parser) parseReturn(retTok scanner.Token) (ast.Node, error) {
method parseFor (line 1416) | func (p *Parser) parseFor(it scanner.Token) (ast.Node, error) {
method parseComment (line 1495) | func (p *Parser) parseComment(it scanner.Token) (ast.Node, error) {
method parseError (line 1499) | func (p *Parser) parseError(it scanner.Token) (ast.Node, error) {
type parserFn (line 29) | type parserFn
type exprConfig (line 31) | type exprConfig struct
function NewParser (line 40) | func NewParser(name, content string) *Parser {
function newParserError (line 1503) | func newParserError(item scanner.Token, name, format string, args ...int...
function isValidArgument (line 1514) | func isValidArgument(t scanner.Token) bool {
function isFuncall (line 1528) | func isFuncall(tok, next token.Token) bool {
function isAssignment (line 1533) | func isAssignment(tok token.Token) bool {
function isExpr (line 1540) | func isExpr(tok token.Token) bool {
FILE: parser/parse_fmt_test.go
type fmtTestTable (line 5) | type fmtTestTable struct
function testFmt (line 9) | func testFmt(input string, expected string, t *testing.T) {
function testFmtTable (line 27) | func testFmtTable(testTable []fmtTestTable, t *testing.T) {
function TestFmtVariables (line 33) | func TestFmtVariables(t *testing.T) {
function TestFmtGroupVariables (line 92) | func TestFmtGroupVariables(t *testing.T) {
function TestFmtFn (line 119) | func TestFmtFn(t *testing.T) {
function TestFmtImports (line 148) | func TestFmtImports(t *testing.T) {
function TestFmtFnComments (line 178) | func TestFmtFnComments(t *testing.T) {
function TestFmtSamples (line 204) | func TestFmtSamples(t *testing.T) {
function TestFmtPipes (line 303) | func TestFmtPipes(t *testing.T) {
FILE: parser/parse_regression_test.go
function init (line 10) | func init() {
function TestParseIssue22 (line 14) | func TestParseIssue22(t *testing.T) {
function TestParseIssue38 (line 75) | func TestParseIssue38(t *testing.T) {
function TestParseIssue43 (line 92) | func TestParseIssue43(t *testing.T) {
function TestParseIssue68 (line 157) | func TestParseIssue68(t *testing.T) {
function TestParseIssue69 (line 185) | func TestParseIssue69(t *testing.T) {
function TestParseImportIssue94 (line 211) | func TestParseImportIssue94(t *testing.T) {
function TestParseIssue108 (line 221) | func TestParseIssue108(t *testing.T) {
function TestParseIssue123 (line 249) | func TestParseIssue123(t *testing.T) {
FILE: parser/parse_test.go
function parserTest (line 11) | func parserTest(name, content string, expected *ast.Tree, t *testing.T, ...
function parserTestFail (line 53) | func parserTestFail(t *testing.T, execStr string) {
function TestParseSimple (line 69) | func TestParseSimple(t *testing.T) {
function TestParseReverseGetSame (line 93) | func TestParseReverseGetSame(t *testing.T) {
function TestParsePipe (line 109) | func TestParsePipe(t *testing.T) {
function TestBasicSetEnvAssignment (line 129) | func TestBasicSetEnvAssignment(t *testing.T) {
function TestBasicAssignment (line 187) | func TestBasicAssignment(t *testing.T) {
function TestVarAssignment (line 230) | func TestVarAssignment(t *testing.T) {
function TestParseMultipleAssign (line 255) | func TestParseMultipleAssign(t *testing.T) {
function TestParseMultipleExecAssignment (line 285) | func TestParseMultipleExecAssignment(t *testing.T) {
function TestParseInvalidIndexing (line 333) | func TestParseInvalidIndexing(t *testing.T) {
function TestParseListAssignment (line 372) | func TestParseListAssignment(t *testing.T) {
function TestParseListOfListsAssignment (line 403) | func TestParseListOfListsAssignment(t *testing.T) {
function TestParseCmdAssignment (line 443) | func TestParseCmdAssignment(t *testing.T) {
function TestParseInvalidEmpty (line 465) | func TestParseInvalidEmpty(t *testing.T) {
function TestParsePathCommand (line 476) | func TestParsePathCommand(t *testing.T) {
function TestParseWithShebang (line 488) | func TestParseWithShebang(t *testing.T) {
function TestParseEmptyFile (line 505) | func TestParseEmptyFile(t *testing.T) {
function TestParseSingleCommand (line 513) | func TestParseSingleCommand(t *testing.T) {
function TestParseRedirectSimple (line 521) | func TestParseRedirectSimple(t *testing.T) {
function TestParseRedirectWithLocation (line 547) | func TestParseRedirectWithLocation(t *testing.T) {
function TestParseRedirectMultiples (line 562) | func TestParseRedirectMultiples(t *testing.T) {
function TestParseCommandWithStringsEqualsNot (line 581) | func TestParseCommandWithStringsEqualsNot(t *testing.T) {
function TestParseCommandSeparatedBySemicolon (line 598) | func TestParseCommandSeparatedBySemicolon(t *testing.T) {
function TestParseStringNotFinished (line 613) | func TestParseStringNotFinished(t *testing.T) {
function TestParseCd (line 628) | func TestParseCd(t *testing.T) {
function TestParseConcatOfIndexedVar (line 743) | func TestParseConcatOfIndexedVar(t *testing.T) {
function TestParseRfork (line 784) | func TestParseRfork(t *testing.T) {
function TestParseRforkWithBlock (line 796) | func TestParseRforkWithBlock(t *testing.T) {
function TestUnpairedRforkBlocks (line 826) | func TestUnpairedRforkBlocks(t *testing.T) {
function TestParseImport (line 837) | func TestParseImport(t *testing.T) {
function TestParseIf (line 857) | func TestParseIf(t *testing.T) {
function TestParseFuncall (line 905) | func TestParseFuncall(t *testing.T) {
function TestParseFuncallInvalid (line 994) | func TestParseFuncallInvalid(t *testing.T) {
function TestParseIfFnInv (line 1013) | func TestParseIfFnInv(t *testing.T) {
function TestParseIfLvariable (line 1064) | func TestParseIfLvariable(t *testing.T) {
function TestParseIfRvariable (line 1089) | func TestParseIfRvariable(t *testing.T) {
function TestParseIfElse (line 1114) | func TestParseIfElse(t *testing.T) {
function TestParseIfElseIf (line 1150) | func TestParseIfElseIf(t *testing.T) {
function TestParseFnBasic (line 1210) | func TestParseFnBasic(t *testing.T) {
function TestParseInlineFnDecl (line 1297) | func TestParseInlineFnDecl(t *testing.T) {
function TestParseBindFn (line 1337) | func TestParseBindFn(t *testing.T) {
function TestParseRedirectionVariable (line 1348) | func TestParseRedirectionVariable(t *testing.T) {
function TestParseReturn (line 1363) | func TestParseReturn(t *testing.T) {
function TestParseIfInvalid (line 1446) | func TestParseIfInvalid(t *testing.T) {
function TestParseFor (line 1456) | func TestParseFor(t *testing.T) {
function TestParseVariableIndexing (line 1508) | func TestParseVariableIndexing(t *testing.T) {
function TestParseMultilineCmdExec (line 1553) | func TestParseMultilineCmdExec(t *testing.T) {
function TestParseMultilineCmdAssign (line 1594) | func TestParseMultilineCmdAssign(t *testing.T) {
function TestMultiPipe (line 1616) | func TestMultiPipe(t *testing.T) {
function TestFnVariadic (line 1658) | func TestFnVariadic(t *testing.T) {
function TestParseValidDotdotdot (line 1688) | func TestParseValidDotdotdot(t *testing.T) {
function TestParseInvalidDotdotdot (line 1705) | func TestParseInvalidDotdotdot(t *testing.T) {
function TestFunctionPipes (line 1722) | func TestFunctionPipes(t *testing.T) {
FILE: scanner/examples_test.go
function Example (line 9) | func Example() {
FILE: scanner/lex.go
type Token (line 14) | type Token struct
method Type (line 50) | func (i Token) Type() token.Token { return i.typ }
method Value (line 51) | func (i Token) Value() string { return i.val }
method String (line 53) | func (i Token) String() string {
type stateFn (line 21) | type stateFn
type Lexer (line 24) | type Lexer struct
method run (line 69) | func (l *Lexer) run() {
method emitVal (line 80) | func (l *Lexer) emitVal(t token.Token, val string, line, column int) {
method emit (line 93) | func (l *Lexer) emit(t token.Token) {
method peek (line 107) | func (l *Lexer) peek() rune {
method next (line 114) | func (l *Lexer) next() rune {
method ignore (line 138) | func (l *Lexer) ignore() {
method backup (line 145) | func (l *Lexer) backup() {
method acceptRun (line 158) | func (l *Lexer) acceptRun(valid string) {
method errorf (line 167) | func (l *Lexer) errorf(format string, args ...interface{}) stateFn {
constant eof (line 47) | eof = -1
function Lex (line 194) | func Lex(name, input string) *Lexer {
function lexStart (line 206) | func lexStart(l *Lexer) stateFn {
function absorbIdentifier (line 432) | func absorbIdentifier(l *Lexer) {
function absorbArgument (line 446) | func absorbArgument(l *Lexer) {
function scanIdentifier (line 460) | func scanIdentifier(l *Lexer) string {
function lexQuote (line 466) | func lexQuote(l *Lexer) stateFn {
function lexComment (line 529) | func lexComment(l *Lexer) stateFn {
function lexSpace (line 550) | func lexSpace(l *Lexer) stateFn {
function ignoreSpaces (line 555) | func ignoreSpaces(l *Lexer) {
function isSpace (line 569) | func isSpace(r rune) bool {
function isArgument (line 573) | func isArgument(r rune) bool {
function isIdentifier (line 581) | func isIdentifier(r rune) bool {
function isAlpha (line 586) | func isAlpha(r rune) bool {
function isEndOfLine (line 591) | func isEndOfLine(r rune) bool {
FILE: scanner/lex_regression_test.go
function TestLexerIssue34 (line 9) | func TestLexerIssue34(t *testing.T) {
function TestLexerIssue21 (line 24) | func TestLexerIssue21(t *testing.T) {
function TestLexerIssue22 (line 36) | func TestLexerIssue22(t *testing.T) {
function TestLexerIssue19 (line 76) | func TestLexerIssue19(t *testing.T) {
function TestLexerIssue38 (line 99) | func TestLexerIssue38(t *testing.T) {
function TestLexerIssue43 (line 116) | func TestLexerIssue43(t *testing.T) {
function TestLexerIssue68 (line 155) | func TestLexerIssue68(t *testing.T) {
function TestLexerIssue85 (line 171) | func TestLexerIssue85(t *testing.T) {
function TestLexerIssue69 (line 184) | func TestLexerIssue69(t *testing.T) {
function TestLexerIssue127 (line 201) | func TestLexerIssue127(t *testing.T) {
FILE: scanner/lex_test.go
function testTable (line 11) | func testTable(name, content string, expected []Token, t *testing.T) {
function TestLexerCommandStringArgs (line 62) | func TestLexerCommandStringArgs(t *testing.T) {
function TestLexerTokenToString (line 77) | func TestLexerTokenToString(t *testing.T) {
function TestLexerShebangOnly (line 115) | func TestLexerShebangOnly(t *testing.T) {
function TestLexerSimpleSetEnvAssignment (line 129) | func TestLexerSimpleSetEnvAssignment(t *testing.T) {
function TestLexerSimpleAssignment (line 140) | func TestLexerSimpleAssignment(t *testing.T) {
function TestLexerListAssignment (line 236) | func TestLexerListAssignment(t *testing.T) {
function TestLexerListOfLists (line 304) | func TestLexerListOfLists(t *testing.T) {
function TestLexerInvalidAssignments (line 350) | func TestLexerInvalidAssignments(t *testing.T) {
function TestLexerSimpleCommand (line 363) | func TestLexerSimpleCommand(t *testing.T) {
function TestLexerPipe (line 445) | func TestLexerPipe(t *testing.T) {
function TestPipeFunctions (line 508) | func TestPipeFunctions(t *testing.T) {
function TestLexerUnquoteArg (line 528) | func TestLexerUnquoteArg(t *testing.T) {
function TestLexerDashedCommand (line 557) | func TestLexerDashedCommand(t *testing.T) {
function TestLexerPathCommand (line 567) | func TestLexerPathCommand(t *testing.T) {
function TestLexerInvalidBlock (line 578) | func TestLexerInvalidBlock(t *testing.T) {
function TestLexerQuotedStringNotFinished (line 587) | func TestLexerQuotedStringNotFinished(t *testing.T) {
function TestLexerVariousCommands (line 605) | func TestLexerVariousCommands(t *testing.T) {
function TestLexerRfork (line 628) | func TestLexerRfork(t *testing.T) {
function TestLexerSomethingIdontcareanymore (line 657) | func TestLexerSomethingIdontcareanymore(t *testing.T) {
function TestLexerBuiltinCd (line 671) | func TestLexerBuiltinCd(t *testing.T) {
function TestLexerRedirectSimple (line 741) | func TestLexerRedirectSimple(t *testing.T) {
function TestLexerRedirectMap (line 784) | func TestLexerRedirectMap(t *testing.T) {
function TestLexerRedirectMapToLocation (line 816) | func TestLexerRedirectMapToLocation(t *testing.T) {
function TestLexerRedirectMultipleMaps (line 862) | func TestLexerRedirectMultipleMaps(t *testing.T) {
function TestLexerImport (line 920) | func TestLexerImport(t *testing.T) {
function TestLexerSimpleIf (line 931) | func TestLexerSimpleIf(t *testing.T) {
function TestLexerIfWithConcat (line 972) | func TestLexerIfWithConcat(t *testing.T) {
function TestLexerIfWithFuncInvocation (line 994) | func TestLexerIfWithFuncInvocation(t *testing.T) {
function TestLexerIfElse (line 1017) | func TestLexerIfElse(t *testing.T) {
function TestLexerIfElseIf (line 1038) | func TestLexerIfElseIf(t *testing.T) {
function TestLexerFnBasic (line 1078) | func TestLexerFnBasic(t *testing.T) {
function TestLexerFuncall (line 1189) | func TestLexerFuncall(t *testing.T) {
function TestLexerAssignCmdOut (line 1270) | func TestLexerAssignCmdOut(t *testing.T) {
function TestLexerMultipleAssignCmdOut (line 1282) | func TestLexerMultipleAssignCmdOut(t *testing.T) {
function TestMultipleAssignments (line 1324) | func TestMultipleAssignments(t *testing.T) {
function TestLexerBindFn (line 1340) | func TestLexerBindFn(t *testing.T) {
function TestLexerRedirectionNetwork (line 1353) | func TestLexerRedirectionNetwork(t *testing.T) {
function TestLexerDump (line 1369) | func TestLexerDump(t *testing.T) {
function TestLexerReturn (line 1397) | func TestLexerReturn(t *testing.T) {
function TestLexerFor (line 1526) | func TestLexerFor(t *testing.T) {
function TestLexerFnAsFirstClass (line 1582) | func TestLexerFnAsFirstClass(t *testing.T) {
function TestLexerListIndexing (line 1635) | func TestLexerListIndexing(t *testing.T) {
function TestLexerMultilineCmdExecution (line 1712) | func TestLexerMultilineCmdExecution(t *testing.T) {
function TestLexerMultilineCmdAssign (line 1754) | func TestLexerMultilineCmdAssign(t *testing.T) {
function TestLexerCommandDelimiter (line 1809) | func TestLexerCommandDelimiter(t *testing.T) {
function TestLexerLongAssignment (line 1824) | func TestLexerLongAssignment(t *testing.T) {
function TestLexerVarArgs (line 1858) | func TestLexerVarArgs(t *testing.T) {
function TestLexerVar (line 1909) | func TestLexerVar(t *testing.T) {
FILE: sh/obj.go
constant StringType (line 7) | StringType objType = iota + 1
constant FnType (line 8) | FnType
constant ListType (line 9) | ListType
type objType (line 13) | type objType
method Type (line 67) | func (o objType) Type() objType {
type Obj (line 15) | type Obj interface
type ListObj (line 20) | type ListObj struct
method Len (line 116) | func (o *ListObj) Len() int {
method Set (line 120) | func (o *ListObj) Set(index int, value Obj) error {
method Get (line 132) | func (o *ListObj) Get(index int) (Obj, error) {
method List (line 143) | func (o *ListObj) List() []Obj { return o.list }
method String (line 145) | func (o *ListObj) String() string {
type FnObj (line 25) | type FnObj struct
method Fn (line 105) | func (o *FnObj) Fn() FnDef { return o.fn }
method String (line 107) | func (o *FnObj) String() string { return fmt.Sprintf("<fn %s>", o.fn.N...
type StrObj (line 30) | type StrObj struct
method Str (line 78) | func (o *StrObj) Str() string { return string(o.runes) }
method String (line 80) | func (o *StrObj) String() string { return o.Str() }
method Get (line 82) | func (o *StrObj) Get(index int) (Obj, error) {
method Len (line 94) | func (o *StrObj) Len() int {
type Collection (line 35) | type Collection interface
type WriteableCollection (line 40) | type WriteableCollection interface
function NewCollection (line 45) | func NewCollection(o Obj) (Collection, error) {
function NewWriteableCollection (line 56) | func NewWriteableCollection(o Obj) (WriteableCollection, error) {
function NewStrObj (line 71) | func NewStrObj(val string) *StrObj {
function NewFnObj (line 98) | func NewFnObj(val FnDef) *FnObj {
function NewListObj (line 109) | func NewListObj(val []Obj) *ListObj {
FILE: sh/objtype_string.go
constant _objType_name (line 7) | _objType_name = "StringTypeFnTypeListType"
method String (line 11) | func (i objType) String() string {
FILE: sh/shell.go
type Runner (line 6) | type Runner interface
type FnArg (line 24) | type FnArg struct
type Fn (line 29) | type Fn interface
type FnDef (line 38) | type FnDef interface
function NewFnArg (line 45) | func NewFnArg(name string, isVariadic bool) FnArg {
FILE: spec_test.go
function TestSpecificationIsSane (line 13) | func TestSpecificationIsSane(t *testing.T) {
FILE: stdbin/mkdir/main.go
function mkdirs (line 8) | func mkdirs(dirnames []string) error {
function main (line 18) | func main() {
FILE: stdbin/mkdir/mkdir_test.go
type testcase (line 11) | type testcase struct
function testMkdir (line 15) | func testMkdir(t *testing.T, tc testcase) {
function TestMkdir (line 41) | func TestMkdir(t *testing.T) {
FILE: stdbin/pwd/main.go
function main (line 8) | func main() {
FILE: stdbin/strings/main.go
function main (line 9) | func main() {
FILE: stdbin/strings/strings.go
function Do (line 10) | func Do(input io.Reader, minTextSize uint) *bufio.Scanner {
function searchstrings (line 21) | func searchstrings(input io.Reader, minTextSize uint, output *io.PipeWri...
type byteType (line 92) | type byteType
type wordSearcher (line 94) | type wordSearcher struct
method next (line 107) | func (w *wordSearcher) next(b byte) ([]byte, bool) {
method nextRune (line 114) | func (w *wordSearcher) nextRune(b byte) ([]byte, bool) {
method resetRuneSearch (line 152) | func (w *wordSearcher) resetRuneSearch() {
method nextASCII (line 157) | func (w *wordSearcher) nextASCII(b byte) ([]byte, bool) {
method startRuneSearch (line 175) | func (w *wordSearcher) startRuneSearch(b byte) {
method writeOnBuffer (line 180) | func (w *wordSearcher) writeOnBuffer(b ...byte) {
method writeOnPossibleRune (line 184) | func (w *wordSearcher) writeOnPossibleRune(b byte) {
method bufferLenInRunes (line 188) | func (w *wordSearcher) bufferLenInRunes() uint {
method flushBuffer (line 192) | func (w *wordSearcher) flushBuffer() ([]byte, bool) {
constant binaryType (line 102) | binaryType byteType = iota
constant asciiType (line 103) | asciiType
constant runeStartType (line 104) | runeStartType
function bytetype (line 205) | func bytetype(b byte) byteType {
FILE: stdbin/strings/strings_test.go
function TestStrings (line 14) | func TestStrings(t *testing.T) {
function TestStringsReadErrorOnFirstByte (line 374) | func TestStringsReadErrorOnFirstByte(t *testing.T) {
function TestStringsReadErrorOnSecondByte (line 382) | func TestStringsReadErrorOnSecondByte(t *testing.T) {
function TestStringsReadErrorAfterValidUTF8StartingByte (line 396) | func TestStringsReadErrorAfterValidUTF8StartingByte(t *testing.T) {
function TestStringsReadCanReturnEOFWithData (line 410) | func TestStringsReadCanReturnEOFWithData(t *testing.T) {
constant runestart (line 431) | runestart byte = 0xC2
type FakeReader (line 433) | type FakeReader struct
method Read (line 437) | func (f *FakeReader) Read(d []byte) (int, error) {
function newFakeReader (line 444) | func newFakeReader(read func([]byte) (int, error)) *FakeReader {
function assertScannerFails (line 448) | func assertScannerFails(t *testing.T, scanner *bufio.Scanner, expectedIt...
function newBinary (line 463) | func newBinary(size uint) []byte {
FILE: stdbin/write/fd.go
function specialFile (line 10) | func specialFile(path string) (io.WriteCloser, bool) {
FILE: stdbin/write/fd_windows.go
function specialFile (line 8) | func specialFile(path string) (io.WriteCloser, bool) {
FILE: stdbin/write/main.go
function fatal (line 12) | func fatal(msg string) {
function main (line 17) | func main() {
FILE: stdbin/write/write.go
function toabs (line 9) | func toabs(path string) (string, error) {
function outfd (line 20) | func outfd(fname string) (io.WriteCloser, error) {
function write (line 39) | func write(fname string, in io.Reader) (err error) {
FILE: tests/cfg.go
function init (line 24) | func init() {
FILE: tests/internal/assert/equal.go
function EqualStrings (line 8) | func EqualStrings(t *testing.T, want string, got string) {
function ContainsString (line 15) | func ContainsString(t *testing.T, str string, sub string) {
FILE: tests/internal/assert/error.go
function NoError (line 5) | func NoError(t *testing.T, err error, operation string) {
FILE: tests/internal/sh/shell.go
function Exec (line 16) | func Exec(
FILE: tests/internal/tester/tester.go
type TestCase (line 12) | type TestCase struct
function Run (line 20) | func Run(t *testing.T, nashcmd string, cases ...TestCase) {
FILE: tests/listindex_test.go
function TestListIndexing (line 9) | func TestListIndexing(t *testing.T) {
FILE: tests/stringindex_test.go
function TestStringIndexing (line 9) | func TestStringIndexing(t *testing.T) {
function TestStringIndexingASCII (line 43) | func TestStringIndexingASCII(t *testing.T) {
function TestStringIndexingNonASCII (line 93) | func TestStringIndexingNonASCII(t *testing.T) {
FILE: token/token.go
type Token (line 6) | type Token
method String (line 152) | func (tok Token) String() string {
type FileInfo (line 8) | type FileInfo struct
method Line (line 149) | func (info FileInfo) Line() int { return info.line }
method Column (line 150) | func (info FileInfo) Column() int { return info.column }
constant Illegal (line 14) | Illegal Token = iota + 1
constant EOF (line 15) | EOF
constant Comment (line 16) | Comment
constant literal_beg (line 18) | literal_beg
constant Ident (line 20) | Ident
constant String (line 21) | String
constant Number (line 22) | Number
constant Arg (line 23) | Arg
constant literal_end (line 25) | literal_end
constant operator_beg (line 27) | operator_beg
constant Assign (line 29) | Assign
constant AssignCmd (line 30) | AssignCmd
constant Equal (line 31) | Equal
constant NotEqual (line 32) | NotEqual
constant Plus (line 33) | Plus
constant Minus (line 34) | Minus
constant Gt (line 35) | Gt
constant Lt (line 36) | Lt
constant Colon (line 38) | Colon
constant Semicolon (line 39) | Semicolon
constant operator_end (line 41) | operator_end
constant LBrace (line 43) | LBrace
constant RBrace (line 44) | RBrace
constant LParen (line 45) | LParen
constant RParen (line 46) | RParen
constant LBrack (line 47) | LBrack
constant RBrack (line 48) | RBrack
constant Pipe (line 49) | Pipe
constant Comma (line 51) | Comma
constant Dotdotdot (line 52) | Dotdotdot
constant Variable (line 54) | Variable
constant keyword_beg (line 56) | keyword_beg
constant Import (line 58) | Import
constant SetEnv (line 59) | SetEnv
constant ShowEnv (line 60) | ShowEnv
constant BindFn (line 61) | BindFn
constant Dump (line 62) | Dump
constant Return (line 63) | Return
constant If (line 64) | If
constant Else (line 65) | Else
constant For (line 66) | For
constant Rfork (line 67) | Rfork
constant Fn (line 68) | Fn
constant Var (line 69) | Var
constant keyword_end (line 71) | keyword_end
function init (line 125) | func init() {
function Lookup (line 132) | func Lookup(ident string) Token {
function IsKeyword (line 140) | func IsKeyword(t Token) bool {
function NewFileInfo (line 148) | func NewFileInfo(l, c int) FileInfo { return FileInfo{l, c} }
FILE: vendor/golang.org/x/exp/ebnf/ebnf.go
type errorList (line 36) | type errorList
method Err (line 38) | func (list errorList) Err() error {
method Error (line 45) | func (list errorList) Error() string {
function newError (line 55) | func newError(pos scanner.Position, msg string) error {
type Expression (line 64) | type Expression interface
type Alternative (line 70) | type Alternative
method Pos (line 128) | func (x Alternative) Pos() scanner.Position { return x[0].Pos() }
type Sequence (line 73) | type Sequence
method Pos (line 129) | func (x Sequence) Pos() scanner.Position { return x[0].Pos() }
type Name (line 76) | type Name struct
method Pos (line 130) | func (x *Name) Pos() scanner.Position { return x.StringPos }
type Token (line 82) | type Token struct
method Pos (line 131) | func (x *Token) Pos() scanner.Position { return x.StringPos }
type Range (line 88) | type Range struct
method Pos (line 132) | func (x *Range) Pos() scanner.Position { return x.Begin.Pos() }
type Group (line 93) | type Group struct
method Pos (line 133) | func (x *Group) Pos() scanner.Position { return x.Lparen }
type Option (line 99) | type Option struct
method Pos (line 134) | func (x *Option) Pos() scanner.Position { return x.Lbrack }
type Repetition (line 105) | type Repetition struct
method Pos (line 135) | func (x *Repetition) Pos() scanner.Position { return x.Lbrace }
type Production (line 111) | type Production struct
method Pos (line 136) | func (x *Production) Pos() scanner.Position { return x.Name.Pos() }
type Bad (line 117) | type Bad struct
method Pos (line 137) | func (x *Bad) Pos() scanner.Position { return x.TokPos }
type Grammar (line 125) | type Grammar
function isLexical (line 142) | func isLexical(name string) bool {
type verifier (line 147) | type verifier struct
method error (line 154) | func (v *verifier) error(pos scanner.Position, msg string) {
method push (line 158) | func (v *verifier) push(prod *Production) {
method verifyChar (line 166) | func (v *verifier) verifyChar(x *Token) rune {
method verifyExpr (line 176) | func (v *verifier) verifyExpr(expr Expression, lexical bool) {
method verify (line 222) | func (v *verifier) verify(grammar Grammar, start string) {
function Verify (line 265) | func Verify(grammar Grammar, start string) error {
FILE: vendor/golang.org/x/exp/ebnf/parser.go
type parser (line 13) | type parser struct
method next (line 21) | func (p *parser) next() {
method error (line 27) | func (p *parser) error(pos scanner.Position, msg string) {
method errorExpected (line 31) | func (p *parser) errorExpected(pos scanner.Position, msg string) {
method expect (line 44) | func (p *parser) expect(tok rune) scanner.Position {
method parseIdentifier (line 53) | func (p *parser) parseIdentifier() *Name {
method parseToken (line 60) | func (p *parser) parseToken() *Token {
method parseTerm (line 76) | func (p *parser) parseTerm() (x Expression) {
method parseSequence (line 111) | func (p *parser) parseSequence() Expression {
method parseExpression (line 130) | func (p *parser) parseExpression() Expression {
method parseProduction (line 150) | func (p *parser) parseProduction() *Production {
method parse (line 161) | func (p *parser) parse(filename string, src io.Reader) Grammar {
function Parse (line 186) | func Parse(filename string, src io.Reader) (Grammar, error) {
Condensed preview — 174 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (744K chars).
[
{
"path": ".gitignore",
"chars": 164,
"preview": "./cmd/cnt/cnt\n/cmd/nash/nash\n/coverage.txt\ncmd/nashfmt/nashfmt\ndist\n*.exe\nstdbin/mkdir/mkdir\nstdbin/pwd/pwd\nstdbin/strin"
},
{
"path": ".travis.yml",
"chars": 645,
"preview": "os:\n - linux\n\nlanguage: go\nsudo: false\n\ngo:\n - \"tip\"\n - \"1.12\"\n\ninstall:\n - go get -v golang.org/x/exp/ebnf\n - make"
},
{
"path": "Dockerfile",
"chars": 197,
"preview": "FROM golang:1.12\n\nADD . /go/src/github.com/madlambda/nash\n\nENV NASHPATH /nashpath\nENV NASHROOT /nashroot\n\nRUN cd /go/src"
},
{
"path": "LICENSE",
"chars": 11354,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 1567,
"preview": "ifndef version\nversion=$(shell git rev-list -1 HEAD)\nendif\n\nbuildargs = -ldflags \"-X main.VersionString=$(version)\" -v\n\n"
},
{
"path": "README.md",
"chars": 13108,
"preview": "<!-- mdtocstart -->\n\n# Table of Contents\n\n- [nash](#nash)\n- [Show time!](#show-time)\n - [Useful stuff](#useful-stuff)"
},
{
"path": "_disabled_appveyor.yml",
"chars": 792,
"preview": "version: \"1.0.0.{build}\"\nplatform: x64\nclone_folder: \"c:\\\\gopath\\\\src\\\\github.com\\\\madlambda\\\\nash\"\nenvironment: \n GOPA"
},
{
"path": "ast/doc_test.go",
"chars": 1351,
"preview": "package ast_test\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/madlambda/nash/ast\"\n\t\"github.com/madlambda/nash/token\"\n)\n\nfunc Example_A"
},
{
"path": "ast/node.go",
"chars": 27545,
"preview": "package ast\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/madlambda/nash/token\"\n)\n\nconst (\n\t// RedirMapNoValue indicates the "
},
{
"path": "ast/node_args.go",
"chars": 4366,
"preview": "package ast\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/madlambda/nash/scanner\"\n\t\"github.com/madlambda/nash/token\"\n)\n\n// ArgFromToken"
},
{
"path": "ast/node_fmt.go",
"chars": 13146,
"preview": "package ast\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc (s *StringExpr) String() string {\n\tif s.quoted {\n\t\treturn `\"`"
},
{
"path": "ast/node_fmt_test.go",
"chars": 2640,
"preview": "package ast\n\nimport (\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/token\"\n)\n\nfunc testPrinter(t *testing.T, node Node, expect"
},
{
"path": "ast/nodetype_string.go",
"chars": 879,
"preview": "// Code generated by \"stringer -type=NodeType\"; DO NOT EDIT\n\npackage ast\n\nimport \"fmt\"\n\nconst _NodeType_name = \"NodeSete"
},
{
"path": "ast/tree.go",
"chars": 495,
"preview": "package ast\n\ntype (\n\t// Tree is the AST\n\tTree struct {\n\t\tName string\n\t\tRoot *BlockNode // top-level root of the tree.\n\t}"
},
{
"path": "ast/tree_test.go",
"chars": 581,
"preview": "package ast\n\nimport (\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/token\"\n)\n\n// Test API\nfunc TestTreeCreation(t *testing.T) "
},
{
"path": "cmd/nash/cli.go",
"chars": 4658,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/madlambda/nash\"\n\t\"github.com/madlambda/nash/"
},
{
"path": "cmd/nash/completer.go",
"chars": 2193,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/madlambda/nash\"\n\t\"github.com/madlambda/nash/sh\"\n\t\"github.co"
},
{
"path": "cmd/nash/env.go",
"chars": 606,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\nfunc NashPath() (string, error) {\n\tnashpath := os.Getenv(\"NAS"
},
{
"path": "cmd/nash/env_test.go",
"chars": 2783,
"preview": "package main_test\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\tmain \"github.com/madlambda/nash/cmd/nash\"\n)\n\n"
},
{
"path": "cmd/nash/example.sh",
"chars": 443,
"preview": "#!/usr/bin/env nash\n\n-rm -rf rootfs\n\nrfork upmis {\n mount -t proc proc /proc\n mkdir -p rootfs\n mount -t tmpfs -"
},
{
"path": "cmd/nash/install.go",
"chars": 1940,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\nfunc NashLibDir(nashpath string) string {\n\t//"
},
{
"path": "cmd/nash/install_test.go",
"chars": 8083,
"preview": "package main_test\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\tmain \"github.com/madlambda/nash/cmd/nash\"\n\t"
},
{
"path": "cmd/nash/main.go",
"chars": 2666,
"preview": "// Package main has two sides:\n// - User mode: shell\n// - tool mode: unix socket server for handling namespace operation"
},
{
"path": "cmd/nash/rpc.go",
"chars": 1181,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\n\t\"github.com/madlambda/nash\"\n)\n\nfunc serveConn(sh *nash.Shell, conn ne"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/.travis.yml",
"chars": 260,
"preview": "language: go\ngo:\n - 1.5\n - 1.7\nscript:\n - GOOS=windows go install github.com/chzyer/readline/example/...\n - GOOS=lin"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/CHANGELOG.md",
"chars": 3030,
"preview": "# ChangeLog\n\n### 1.4 - 2016-07-25\n\n* [#60][60] Support dynamic autocompletion\n* Fix ANSI parser on Windows\n* Fix wrong c"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/LICENSE",
"chars": 1074,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Chzyer\n\nPermission is hereby granted, free of charge, to any person obtaining "
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/README.md",
"chars": 12101,
"preview": "[](https://travis-ci.org/chzyer/readline)\n[![Sof"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/ansi_windows.go",
"chars": 5072,
"preview": "// +build windows\n\npackage readline\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"unicode/utf8\"\n\t\"unsafe\"\n)\n\n"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/complete.go",
"chars": 6291,
"preview": "package readline\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n)\n\ntype AutoCompleter interface {\n\t// Readline will pass the w"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/complete_helper.go",
"chars": 3958,
"preview": "package readline\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n)\n\n// Caller type for dynamic completion\ntype DynamicCompleteFunc func(st"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/complete_segment.go",
"chars": 1792,
"preview": "package readline\n\ntype SegmentCompleter interface {\n\t// a\n\t// |- a1\n\t// |--- a11\n\t// |- a2\n\t// b\n\t// input:\n\t// DoTree"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/complete_segment_test.go",
"chars": 3263,
"preview": "package readline\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/chzyer/test\"\n)\n\nfunc rs(s [][]rune) []string {\n\tret := make(["
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/doc/shortcut.md",
"chars": 3361,
"preview": "## Readline Shortcut\n\n`Meta`+`B` means press `Esc` and `n` separately. \nUsers can change that in terminal simulator(i.e"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/example/readline-demo/readline-demo.go",
"chars": 3439,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/chzyer/readline\"\n)\n\n"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/example/readline-im/README.md",
"chars": 152,
"preview": "# readline-im\n\n\nimport \"log\"\n\nfunc main() {\n\trl, err"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/example/readline-multiline/readline-multiline.go",
"chars": 676,
"preview": "package main\n\nimport (\n\t\"strings\"\n\n\t\"github.com/chzyer/readline\"\n)\n\nfunc main() {\n\trl, err := readline.NewEx(&readline.C"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/example/readline-pass-strength/readline-pass-strength.go",
"chars": 2465,
"preview": "// This is a small example using readline to read a password\n// and check it's strength while typing using the zxcvbn li"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/example/readline-remote/readline-remote-client/client.go",
"chars": 155,
"preview": "package main\n\nimport \"github.com/chzyer/readline\"\n\nfunc main() {\n\tif err := readline.DialRemote(\"tcp\", \":12344\"); err !="
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/example/readline-remote/readline-remote-server/server.go",
"chars": 417,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/chzyer/readline\"\n)\n\nfunc main() {\n\tcfg := &readline.Config{\n\t\tPrompt: \"readl"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/history.go",
"chars": 5879,
"preview": "package readline\n\nimport (\n\t\"bufio\"\n\t\"container/list\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n)\n\ntype hisItem struct {\n\tSource "
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/operation.go",
"chars": 8862,
"preview": "package readline\n\nimport (\n\t\"errors\"\n\t\"io\"\n)\n\nvar (\n\tErrInterrupt = errors.New(\"Interrupt\")\n)\n\ntype InterruptError struc"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/password.go",
"chars": 592,
"preview": "package readline\n\ntype opPassword struct {\n\to *Operation\n\tbackupCfg *Config\n}\n\nfunc newOpPassword(o *Operation) "
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/rawreader_windows.go",
"chars": 2320,
"preview": "// +build windows\n\npackage readline\n\nimport \"unsafe\"\n\nconst (\n\tVK_CANCEL = 0x03\n\tVK_BACK = 0x08\n\tVK_TAB = 0x0"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/readline.go",
"chars": 5754,
"preview": "// Readline is a pure go implementation for GNU-Readline kind library.\n//\n// example:\n// \trl, err := readline.New(\"> \")\n"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/readline_test.go",
"chars": 329,
"preview": "package readline\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestRace(t *testing.T) {\n\trl, err := NewEx(&Config{})\n\tif err != n"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/remote.go",
"chars": 8539,
"preview": "package readline\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\ntype"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/runebuf.go",
"chars": 9652,
"preview": "package readline\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n)\n\ntype runeBufferBck struct {\n\tbuf []rune\n\tidx in"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/runes/runes.go",
"chars": 2588,
"preview": "// deprecated.\n// see https://github.com/chzyer/readline/issues/43\n// use github.com/chzyer/readline/runes.go\npackage ru"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/runes/runes_test.go",
"chars": 1339,
"preview": "package runes\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype twidth struct {\n\tr []rune\n\tlength int\n}\n\nfunc TestRuneWidth(t"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/runes.go",
"chars": 3839,
"preview": "package readline\n\nimport (\n\t\"bytes\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n)\n\nvar runes = Runes{}\nvar TabWidth = 4\n\ntype Runes struc"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/runes_test.go",
"chars": 1348,
"preview": "package readline\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype twidth struct {\n\tr []rune\n\tlength int\n}\n\nfunc TestRuneWidt"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/std.go",
"chars": 2203,
"preview": "package readline\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"sync\"\n)\n\nvar (\n\tStdin io.ReadCloser = os.Stdin\n\tStdout io.WriteCloser = os.St"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/std_windows.go",
"chars": 141,
"preview": "// +build windows\n\npackage readline\n\nfunc init() {\n\tStdin = NewRawReader()\n\tStdout = NewANSIWriter(Stdout)\n\tStderr = New"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/term.go",
"chars": 4274,
"preview": "// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/term_bsd.go",
"chars": 332,
"preview": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/term_linux.go",
"chars": 457,
"preview": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/term_windows.go",
"chars": 4406,
"preview": "// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/terminal.go",
"chars": 3974,
"preview": "package readline\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\ntype Terminal struct {\n\tcf"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/utils.go",
"chars": 4630,
"preview": "package readline\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"container/list\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\t\"unic"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/utils_test.go",
"chars": 17,
"preview": "package readline\n"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/utils_unix.go",
"chars": 1685,
"preview": "// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd\n\npackage readline\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"os/signal\"\n"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/utils_windows.go",
"chars": 554,
"preview": "// +build windows\n\npackage readline\n\nimport (\n\t\"io\"\n\t\"syscall\"\n)\n\nfunc SuspendMe() {\n}\n\nfunc GetStdin() int {\n\treturn in"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/vim.go",
"chars": 2814,
"preview": "package readline\n\nconst (\n\tVIM_NORMAL = iota\n\tVIM_INSERT\n\tVIM_VISUAL\n)\n\ntype opVim struct {\n\tcfg *Config\n\top *O"
},
{
"path": "cmd/nash/vendor/github.com/chzyer/readline/windows_api.go",
"chars": 3052,
"preview": "// +build windows\n\npackage readline\n\nimport (\n\t\"reflect\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\nvar (\n\tkernel = NewKernel()\n\tstdout = u"
},
{
"path": "cmd/nash/vendor.sh",
"chars": 724,
"preview": "#!/usr/bin/env nash\n\nfn vendor() {\n cwdir <= pwd | xargs echo -n\n vendordir = $cwdir + \"/vendor\"\n r"
},
{
"path": "cmd/nashfmt/main.go",
"chars": 1314,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"os\"\n\n\t\"github.com/madlambda/nash/parser\"\n)\n\nvar (\n\toverwrite "
},
{
"path": "docs/interactive.md",
"chars": 4456,
"preview": "<!-- mdtocstart -->\n\n# Table of Contents\n\n- [Line mode](#line-mode)\n- [Autocomplete](#autocomplete)\n- [Hooks](#hooks)\n- "
},
{
"path": "docs/reference.md",
"chars": 4581,
"preview": "<!-- mdtocstart -->\n\n# Table of Contents\n\n- [Command line arguments](#command-line-arguments)\n- [Flow control](#flow-con"
},
{
"path": "docs/stdlib/fmt.md",
"chars": 244,
"preview": "<!-- mdtocstart -->\n\n# Table of Contents\n\n- [fmt](#fmt)\n - [fmt_println](#fmtprintln)\n\n<!-- mdtocend -->\n\n# fmt\n\n## f"
},
{
"path": "errors/error.go",
"chars": 1581,
"preview": "package errors\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/madlambda/nash/ast\"\n\t\"github.com/madlambda/nash/scanner\"\n)\n\ntype (\n\tNashEr"
},
{
"path": "examples/append.sh",
"chars": 219,
"preview": "#!/usr/bin/env nash\n\nvar example_list = ()\necho \"appending string 1\"\nexample_list <= append($example_list, \"1\")\necho $ex"
},
{
"path": "examples/args.sh",
"chars": 217,
"preview": "#!/usr/bin/env nash\n\nprint(\"iterating through the arguments list\\n\\n\")\nfor arg in $ARGS {\n print(\"%s\\n\", $arg)\n}\n\npri"
},
{
"path": "examples/init",
"chars": 930,
"preview": "#!/usr/bin/env nash\n\n# Simple init script for you base your own\n# For a more complete and organized .nash see:\n# https:/"
},
{
"path": "examples/len.sh",
"chars": 155,
"preview": "#!/usr/bin/env nash\n\necho \"args: \"\necho $ARGS\n\nif len($ARGS) == \"1\" {\n echo \"one parameter passed\"\n} else {\n "
},
{
"path": "examples_test.go",
"chars": 633,
"preview": "package nash_test\n\nimport (\n\t\"os\"\n\t\"io/ioutil\"\n\t\n\t\"github.com/madlambda/nash\"\n)\n\nfunc Example() {\n\n\tnashpath,cleanup := "
},
{
"path": "fuzz.go",
"chars": 267,
"preview": "// +build gofuzz\n\npackage nash\n\nimport \"github.com/madlambda/nash/parser\"\n\nfunc Fuzz(data []byte) int {\n\tp := parser.New"
},
{
"path": "hack/check.sh",
"chars": 267,
"preview": "#!/bin/bash\n\nset -e\n\ngo test -race -coverprofile=coverage.txt ./...\n\necho \"running stdlib and stdbin tests\"\ntests=$(find"
},
{
"path": "hack/install/unix.sh",
"chars": 666,
"preview": "#!/bin/bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nwhich wget >/dev/null || { echo \"wget not installed\"; exit"
},
{
"path": "hack/releaser.sh",
"chars": 1948,
"preview": "#!/usr/bin/env nash\n\nif len($ARGS) != \"2\" {\n\tprint(\"usage: %s <version>\\n\\n\", $ARGS[0])\n\texit(\"1\")\n}\n\nvar version = $ARG"
},
{
"path": "internal/sh/builtin/append.go",
"chars": 1071,
"preview": "package builtin\n\nimport (\n\t\"io\"\n\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/nash/sh\"\n)\n\ntype (\n\tappendFn"
},
{
"path": "internal/sh/builtin/append_test.go",
"chars": 2439,
"preview": "package builtin_test\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\n\t\"github.com/madlambda/nash/internal/sh/internal/fixture\"\n)\n\ntype t"
},
{
"path": "internal/sh/builtin/chdir.go",
"chars": 991,
"preview": "package builtin\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/nash/sh\"\n)\n\ntyp"
},
{
"path": "internal/sh/builtin/doc.go",
"chars": 67,
"preview": "// builtin is where all built in functions resides\npackage builtin\n"
},
{
"path": "internal/sh/builtin/exec_test.go",
"chars": 694,
"preview": "package builtin_test\n\nimport (\n\t\"testing\"\n\t\n\t\"github.com/madlambda/nash/internal/sh/internal/fixture\"\n)\n\nfunc execSucces"
},
{
"path": "internal/sh/builtin/exit.go",
"chars": 961,
"preview": "package builtin\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/nash/sh\"\n)\n"
},
{
"path": "internal/sh/builtin/exit_test.go",
"chars": 1191,
"preview": "package builtin_test\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\t\"testing\"\n)\n\nfunc TestExit(t *testing.T) {\n\ttype exitDesc struct {\n\t\tsc"
},
{
"path": "internal/sh/builtin/format.go",
"chars": 785,
"preview": "package builtin\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/nash/sh\"\n)\n\ntype (\n\tf"
},
{
"path": "internal/sh/builtin/format_test.go",
"chars": 2428,
"preview": "package builtin_test\n\nimport \"testing\"\n\nfunc TestFormat(t *testing.T) {\n\ttype formatDesc struct {\n\t\tscript string\n\t\toutp"
},
{
"path": "internal/sh/builtin/glob.go",
"chars": 1103,
"preview": "package builtin\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"path/filepath\"\n\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/nash"
},
{
"path": "internal/sh/builtin/glob_test.go",
"chars": 2586,
"preview": "package builtin_test\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc setup(t *testing.T) (string, func"
},
{
"path": "internal/sh/builtin/len.go",
"chars": 787,
"preview": "package builtin\n\nimport (\n\t\"io\"\n\t\"strconv\"\n\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/nash/sh\"\n)\n\ntype "
},
{
"path": "internal/sh/builtin/len_test.go",
"chars": 883,
"preview": "package builtin_test\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/internal/sh/internal/fixture\"\n)\n\nfunc Te"
},
{
"path": "internal/sh/builtin/loader.go",
"chars": 874,
"preview": "package builtin\n\nimport (\n\t\"io\"\n\n\t\"github.com/madlambda/nash/sh\"\n)\n\n// Fn is the contract of a built in function, that i"
},
{
"path": "internal/sh/builtin/print.go",
"chars": 762,
"preview": "package builtin\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/nash/sh\"\n)\n\ntype (\n\tp"
},
{
"path": "internal/sh/builtin/print_test.go",
"chars": 1820,
"preview": "package builtin_test\n\nimport \"testing\"\n\nfunc TestPrint(t *testing.T) {\n\ttype printDesc struct {\n\t\tscript string\n\t\toutput"
},
{
"path": "internal/sh/builtin/split.go",
"chars": 2428,
"preview": "package builtin\n\nimport (\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/nash/sh\"\n)\n\ntype "
},
{
"path": "internal/sh/builtin/split_test.go",
"chars": 1850,
"preview": "package builtin_test\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/internal/sh/internal/fixture\"\n)\n\nfunc Te"
},
{
"path": "internal/sh/builtin/testdata/exit.sh",
"chars": 36,
"preview": "#!/usr/bin/env nash\n\nexit($ARGS[1])\n"
},
{
"path": "internal/sh/builtin/testdata/split.sh",
"chars": 123,
"preview": "#!/usr/bin/env nash\n\nvar word = $ARGS[1]\nvar sep = $ARGS[2]\nvar output <= split($word, $sep)\nfor o in $output {\n\techo $o"
},
{
"path": "internal/sh/builtin/testdata/splitfunc.sh",
"chars": 202,
"preview": "#!/usr/bin/env nash\n\nvar word = $ARGS[1]\nvar sep = $ARGS[2]\n\nfn splitter(char) {\n\tif $char == $sep {\n\t\treturn \"0\"\n\t}\n\n\tr"
},
{
"path": "internal/sh/builtin.go",
"chars": 2053,
"preview": "package sh\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/nash/internal/sh/builtin\"\n"
},
{
"path": "internal/sh/cmd.go",
"chars": 2586,
"preview": "package sh\n\nimport (\n\t\"io\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\n\t\"github.com/madlambda/nash/ast\"\n\t\"github.com/madlambda/nash/err"
},
{
"path": "internal/sh/cmd_test.go",
"chars": 11,
"preview": "package sh\n"
},
{
"path": "internal/sh/fncall.go",
"chars": 4640,
"preview": "package sh\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/madlambda/nash/ast\"\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.c"
},
{
"path": "internal/sh/fndef.go",
"chars": 2691,
"preview": "package sh\n\nimport (\n\t\"io\"\n\n\t\"github.com/madlambda/nash/ast\"\n\t\"github.com/madlambda/nash/errors\"\n\t\"github.com/madlambda/"
},
{
"path": "internal/sh/functions_test.go",
"chars": 2837,
"preview": "package sh_test\n\nimport \"testing\"\n\nfunc TestFunctionsClosures(t *testing.T) {\n\tfor _, test := range []execTestCase{\n\t\t{\n"
},
{
"path": "internal/sh/internal/fixture/fixture.go",
"chars": 1007,
"preview": "package fixture\n\nimport (\n\t\"testing\"\n\t\"path/filepath\"\n\t\n\t\"github.com/madlambda/nash\"\n\t\"github.com/madlambda/nash/interna"
},
{
"path": "internal/sh/ioutils_test.go",
"chars": 455,
"preview": "package sh_test\n\nimport (\n\t\"os\"\n\t\"testing\"\n\t\"io/ioutil\"\n)\n\nfunc writeFile(t *testing.T, filename string, data string) {\n"
},
{
"path": "internal/sh/log.go",
"chars": 356,
"preview": "package sh\n\nimport (\n\t\"log\"\n\t\"os\"\n)\n\n// LogFn is the logger type\ntype LogFn func(format string, args ...interface{})\n\n//"
},
{
"path": "internal/sh/rfork.go",
"chars": 250,
"preview": "// +build !linux,!plan9\n\n//\npackage sh\n\nimport (\n\t\"github.com/madlambda/nash/ast\"\n\t\"github.com/madlambda/nash/errors\"\n)\n"
},
{
"path": "internal/sh/rfork_linux.go",
"chars": 4238,
"preview": "// +build linux\n\n// nash provides the execution engine\npackage sh\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strco"
},
{
"path": "internal/sh/rfork_linux_test.go",
"chars": 1978,
"preview": "// +build linux\n\npackage sh\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\t\"syscall\"\n\t\"testing\"\n)\n\nfunc getletters() string {\n\tvar a byt"
},
{
"path": "internal/sh/rfork_plan9.go",
"chars": 800,
"preview": "// +build plan9\n\npackage sh\n\nimport (\n\t\"fmt\"\n\t\"syscall\"\n)\n\nfunc (sh *Shell) executeRfork(rfork *RforkNode) error {\n\tretu"
},
{
"path": "internal/sh/shell.go",
"chars": 55404,
"preview": "package sh\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strconv\""
},
{
"path": "internal/sh/shell_import_test.go",
"chars": 4554,
"preview": "package sh_test\n\nimport (\n\t\"bytes\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/internal/sh\"\n\t\"gi"
},
{
"path": "internal/sh/shell_linux_test.go",
"chars": 2766,
"preview": "// +build linux\n\npackage sh_test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"testing\"\n)\n\nvar (\n"
},
{
"path": "internal/sh/shell_regression_test.go",
"chars": 1082,
"preview": "package sh_test\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"path\"\n\t\"fmt\"\n)\n\nfunc TestExecuteIssue68(t *testing."
},
{
"path": "internal/sh/shell_test.go",
"chars": 45483,
"preview": "package sh_test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strconv\"\n\t\""
},
{
"path": "internal/sh/shell_var_test.go",
"chars": 2559,
"preview": "package sh_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/tests\"\n)\n\nfunc TestVarAssign(t *testing.T) {\n\tf"
},
{
"path": "internal/sh/util.go",
"chars": 1664,
"preview": "package sh\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/madlambda/na"
},
{
"path": "internal/sh/util_test.go",
"chars": 1231,
"preview": "package sh\n\nimport (\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/sh\"\n)\n\nfunc TestBuildEnv(t *testing.T) {\n\tenv := En"
},
{
"path": "internal/testing/fixture/io.go",
"chars": 2468,
"preview": "package fixture\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"math/rand\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n)\n\n// Tmpdir creates a temp"
},
{
"path": "nash.go",
"chars": 5854,
"preview": "// Package nash provides a library to embed the `nash` scripting language\n// within your program or create your own nash"
},
{
"path": "nash_test.go",
"chars": 2343,
"preview": "package nash\n\nimport (\n\t\"bytes\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/sh\"\n\t\"github.com/madlambda/na"
},
{
"path": "parser/parse.go",
"chars": 31087,
"preview": "package parser\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\n\t\"strconv\"\n\n\t\"github.com/madlambda/nash/ast\"\n\t\"github.com/madlambda/nash/err"
},
{
"path": "parser/parse_fmt_test.go",
"chars": 6661,
"preview": "package parser\n\nimport \"testing\"\n\ntype fmtTestTable struct {\n\tinput, expected string\n}\n\nfunc testFmt(input string, expec"
},
{
"path": "parser/parse_regression_test.go",
"chars": 7546,
"preview": "package parser\n\nimport (\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/ast\"\n\t\"github.com/madlambda/nash/token\"\n)\n\nfunc init() "
},
{
"path": "parser/parse_test.go",
"chars": 47688,
"preview": "package parser\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/ast\"\n\t\"github.com/madlambda/nash/token\"\n)\n\nf"
},
{
"path": "proposal/1-scope-management.md",
"chars": 10192,
"preview": "# Proposal: Proper scope management\n\nThis has already been implemented but these docs remain here as some sort\nof ration"
},
{
"path": "proposal/2-concurrency.md",
"chars": 8074,
"preview": "# Proposal: Concurrency on Nash\n\nThere has been some discussion on how to provide concurrency to nash.\nThere is a [discu"
},
{
"path": "scanner/examples_test.go",
"chars": 254,
"preview": "package scanner_test\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/madlambda/nash/scanner\"\n)\n\nfunc Example() {\n\tlex := scanner.Lex(\"-in"
},
{
"path": "scanner/lex.go",
"chars": 10826,
"preview": "// Package scanner is the lexical parser.\npackage scanner\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"gith"
},
{
"path": "scanner/lex_regression_test.go",
"chars": 5813,
"preview": "package scanner\n\nimport (\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/token\"\n)\n\nfunc TestLexerIssue34(t *testing.T) {\n\texpec"
},
{
"path": "scanner/lex_test.go",
"chars": 50523,
"preview": "package scanner\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/madlambda/nash/token\"\n)\nimport \"testing\"\n\nfunc testTable(name,"
},
{
"path": "sh/obj.go",
"chars": 2605,
"preview": "package sh\n\nimport \"fmt\"\n\n//go:generate stringer -type=objType\nconst (\n\tStringType objType = iota + 1\n\tFnType\n\tListType\n"
},
{
"path": "sh/objtype_string.go",
"chars": 385,
"preview": "// Code generated by \"stringer -type=objType\"; DO NOT EDIT\n\npackage sh\n\nimport \"fmt\"\n\nconst _objType_name = \"StringTypeF"
},
{
"path": "sh/shell.go",
"chars": 659,
"preview": "package sh\n\nimport \"io\"\n\ntype (\n\tRunner interface {\n\t\tStart() error\n\t\tWait() error\n\t\tResults() []Obj\n\n\t\tSetArgs([]Obj) e"
},
{
"path": "spec.ebnf",
"chars": 3451,
"preview": "/* Nash program */\nprogram = { statement } .\n\n/* Statement */\nstatement = varDecl | command | fnInv | builtin | comment "
},
{
"path": "spec_test.go",
"chars": 535,
"preview": "package nash\n\nimport (\n\t\"bytes\"\n\t\"io/ioutil\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/tests\"\n\t\"golang.or"
},
{
"path": "stdbin/mkdir/main.go",
"chars": 423,
"preview": "package main \n\nimport (\n\t\"os\"\n\t\"fmt\"\n)\n\nfunc mkdirs(dirnames []string) error {\n\tfor _, d := range dirnames {\n\t\tif err :="
},
{
"path": "stdbin/mkdir/mkdir_test.go",
"chars": 964,
"preview": "package main \n\nimport (\n\t\"testing\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"io/ioutil\"\n\t\"os\"\n)\n\ntype testcase struct {\n\tdirs []string\n"
},
{
"path": "stdbin/pwd/main.go",
"chars": 171,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\twd, err := os.Getwd()\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"e"
},
{
"path": "stdbin/strings/main.go",
"chars": 427,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\n\tconst defaultMinTextSize = 4\n\tvar minTextSize uint\n\n\tflag"
},
{
"path": "stdbin/strings/strings.go",
"chars": 4036,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"unicode/utf8\"\n)\n\nfunc Do(input io.Reader, minTextSize uint) *bufio.Scanne"
},
{
"path": "stdbin/strings/strings_test.go",
"chars": 10788,
"preview": "package main_test\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\n\tstrings \"github.com/madlambda/nash/std"
},
{
"path": "stdbin/write/common_test.sh",
"chars": 228,
"preview": "# common test routines\n\nfn fatal(msg) {\n print($msg)\n exit(\"1\")\n}\n\nfn assert(expected, got, desc) {\n if $expect"
},
{
"path": "stdbin/write/fd.go",
"chars": 249,
"preview": "//+build !windows\n\npackage main\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\nfunc specialFile(path string) (io.WriteCloser, bool) {\n\tif path"
},
{
"path": "stdbin/write/fd_windows.go",
"chars": 174,
"preview": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\nfunc specialFile(path string) (io.WriteCloser, bool) {\n\tif path == \"CON\" { // holy"
},
{
"path": "stdbin/write/main.go",
"chars": 482,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nvar banner = fmt.Sprintf(\"%s <file> <data>\\n\", os.Args[0])\n\nfunc f"
},
{
"path": "stdbin/write/write.go",
"chars": 812,
"preview": "package main\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\nfunc toabs(path string) (string, error) {\n\tif !filepath.IsAbs(pat"
},
{
"path": "stdbin/write/write_linux_test.sh",
"chars": 731,
"preview": "# linux tests of write command\n\nimport \"./common_test.sh\"\n\n# this test uses only the write binary\nsetenv PATH = \"./stdbi"
},
{
"path": "stdbin/write/write_test.sh",
"chars": 870,
"preview": "import \"./common_test.sh\"\n\nsetenv PATH = \"./stdbin/write:/bin\"\n\n# FIXME: we need our mktemp\nvar nonExistentFile = \"./her"
},
{
"path": "stdlib/io.sh",
"chars": 70,
"preview": "\nfn io_println(msg, args...) {\n print($msg + \"\\n\", $args...)\n}\n"
},
{
"path": "stdlib/io_example.sh",
"chars": 115,
"preview": "import io\n\nif len($ARGS) == \"2\" {\n io_println($ARGS[1])\n exit(\"0\")\n}\n\nio_println($ARGS[1], $ARGS[2])\n"
},
{
"path": "stdlib/io_test.sh",
"chars": 787,
"preview": "\nfn run_example(args...) {\n var got, status <= ./cmd/nash/nash ./stdlib/io_example.sh $args\n return $got, "
},
{
"path": "stdlib/map.sh",
"chars": 950,
"preview": "fn map_new() {\n return ()\n}\n\nfn map_get(map, key) {\n return map_get_default($map, $key, \"\")\n}\n\nfn map_iter"
},
{
"path": "stdlib/map_test.sh",
"chars": 1949,
"preview": "import map\n\nfn expect(map, key, want) {\n var got <= map_get($map, $key)\n if $got != $want {\n "
},
{
"path": "testfiles/ex1.sh",
"chars": 31,
"preview": "#!/bin/cnt\n\necho \"hello world\"\n"
},
{
"path": "testfiles/fibonacci.sh",
"chars": 1513,
"preview": "#!/usr/bin/env nash\n\n# Recursive fibonacci implementation to find the value\n# at index n in the sequence.\n\n# Some times:"
},
{
"path": "testfiles/sieve.sh",
"chars": 1355,
"preview": "#!/usr/bin/env nash\n\n# Sieve of Erathostenes\n\nfn lt(a, b) {\n\tvar _, st <= test $a -lt $b\n\n\treturn $st\n}\n\nfn gt(a, b) {\n\t"
},
{
"path": "tests/cfg.go",
"chars": 878,
"preview": "package tests\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nvar (\n\t// Nashcmd is the nash's absolute binary "
},
{
"path": "tests/doc.go",
"chars": 858,
"preview": "// Package tests contains all nash tests that are blackbox.\n// What would be blackbox ? These are tests that are targete"
},
{
"path": "tests/internal/assert/doc.go",
"chars": 66,
"preview": "// Package assert has some common assert functions\npackage assert\n"
},
{
"path": "tests/internal/assert/equal.go",
"chars": 444,
"preview": "package assert\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc EqualStrings(t *testing.T, want string, got string) {\n\t// TODO: c"
},
{
"path": "tests/internal/assert/error.go",
"chars": 157,
"preview": "package assert\n\nimport \"testing\"\n\nfunc NoError(t *testing.T, err error, operation string) {\n\tif err != nil {\n\t\tt.Fatalf("
},
{
"path": "tests/internal/sh/shell.go",
"chars": 1054,
"preview": "// Package shell makes it easier to run nash scripts for test purposes\npackage sh\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/ioutil\"\n"
},
{
"path": "tests/internal/tester/tester.go",
"chars": 1080,
"preview": "// Package tester makes it easy to run multiple\n// script test cases.\npackage tester\n\nimport (\n\t\"testing\"\n\n\t\"github.com/"
},
{
"path": "tests/listindex_test.go",
"chars": 1348,
"preview": "package tests\n\nimport (\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/tests/internal/tester\"\n)\n\nfunc TestListIndexing(t *testi"
},
{
"path": "tests/stringindex_test.go",
"chars": 2349,
"preview": "package tests\n\nimport (\n\t\"testing\"\n\n\t\"github.com/madlambda/nash/tests/internal/tester\"\n)\n\nfunc TestStringIndexing(t *tes"
},
{
"path": "token/token.go",
"chars": 2174,
"preview": "package token\n\nimport \"strconv\"\n\ntype (\n\tToken int\n\n\tFileInfo struct {\n\t\tline, column int\n\t}\n)\n\nconst (\n\tIllegal Token ="
},
{
"path": "vendor/golang.org/x/exp/ebnf/ebnf.go",
"chars": 7245,
"preview": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "vendor/golang.org/x/exp/ebnf/parser.go",
"chars": 3974,
"preview": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
}
]
About this extraction
This page contains the full source code of the NeowayLabs/nash GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 174 files (642.6 KB), approximately 205.8k tokens, and a symbol index with 1605 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.