pax_global_header00006660000000000000000000000064136141100000014474gustar00rootroot0000000000000052 comment=c47dffa6212d01a47824fda05695d4ab627ddfdb direnv-2.21.2/000077500000000000000000000000001361411000000130475ustar00rootroot00000000000000direnv-2.21.2/.github/000077500000000000000000000000001361411000000144075ustar00rootroot00000000000000direnv-2.21.2/.github/ISSUE_TEMPLATE/000077500000000000000000000000001361411000000165725ustar00rootroot00000000000000direnv-2.21.2/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000010031361411000000212560ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: 'Bug' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. **Environment** - OS: [e.g. macOS Mojave, Ubuntu 18.04, ...] - Shell: [e.g. bash, zsh, ...] - Direnv version [e.g. 2.20.0] **Additional context** Add any other context about the problem here. direnv-2.21.2/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011321361411000000223140ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: 'Feature' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. direnv-2.21.2/.github/workflows/000077500000000000000000000000001361411000000164445ustar00rootroot00000000000000direnv-2.21.2/.github/workflows/go.yml000066400000000000000000000013301361411000000175710ustar00rootroot00000000000000name: Go on: push: branches: [ master ] pull_request: {} jobs: build: name: Build runs-on: ${{ matrix.os }} steps: - name: Set up Go 1.13.6 uses: actions/setup-go@v1 with: go-version: 1.13.6 id: go - name: Check out code into the Go module directory uses: actions/checkout@v1 - name: Build env: GO111MODULE: on run: go build -v . - name: Test env: GO111MODULE: on run: go test -v ./... - name: Test Host bash env: GO111MODULE: on run: make test-stdlib test-bash strategy: matrix: os: - ubuntu-latest - macos-latest # TODO: - windows-latest direnv-2.21.2/.github/workflows/nix.yml000066400000000000000000000012471361411000000177710ustar00rootroot00000000000000name: Nix on: push: branches: [ master ] pull_request: {} jobs: build: name: Build runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v1 with: fetch-depth: 1 - uses: cachix/install-nix-action@v6 - name: Build run: nix-build # Run the more comprehensive test suite with nix because we have # access to all the build dependencies. - name: Test run: nix-shell --pure --run "make test" # Test that the release generation works - name: Test Dist run: nix-shell --pure --run "make dist" strategy: matrix: os: [ubuntu-latest, macos-latest] direnv-2.21.2/.gitignore000066400000000000000000000001521361411000000150350ustar00rootroot00000000000000*.sw? .direnv .gopath /direnv /direnv.test /dist /test/config /test/data /test/scenarios/inherited/.envrc direnv-2.21.2/.golangci.yml000066400000000000000000000001571361411000000154360ustar00rootroot00000000000000linters: presets: - bugs - unused enable: - gofmt - golint - misspell - stylecheck direnv-2.21.2/CHANGELOG.md000066400000000000000000000353421361411000000146670ustar00rootroot000000000000002.21.2 / 2020-01-28 ================== Making things stable again. * stdlib: revert the `set -euo pipefail` change. It was causing too many issues for users. * direnv allow: fix the allow migration by also creating the parent target directory. 2.21.1 / 2020-01-26 ================== Fix release * stdlib: fix unused variable in `use node` * stdlib: fix unused variable in `source_up` * test: add stdlib test skeleton * add dist release utility 2.21.0 / 2020-01-25 ================== This is a massive release! ## Highlights You can now hit Ctrl-C during a long reload in bash and zsh and it will not loop anymore. Commands that use `direnv_load` won't fail when there is an output to stdout anymore (eg: `use_nix`). Direnv now also loads files from `.config/direnv/lib/*.sh`. This is intended to be used by third-party tools to augment direnv with their own stdlib functions. The `.envrc` is now loaded with `set -euo pipefail`. This will more likely expose issues with existing `.envrc` files. ## docs * Update README.md (#536) * Add link to asdf-direnv. (#535) * docs: fix invalid link (#533) * adds experimental curl based installer (#539) ## commands * change where the allow files are being stored * direnv status: also show the config * direnv exec: improve the error message * warn if PS1 is being exported * handle SIGINT during export in bash * export: display the full RC path instead of a relative one * direnv exec: the DIR argument is always required (#493) ## build * ci: use GitHub Actions instead of Azure Pipelines * staticcheck (#543) * use go modules * make: handle when /dev/stderr doesn't exist (#491) * site: use jekyll to render the website * Pin nixpkgs to current NixOS 19.09 channel (#526) ## shells * fix elvish hook * Use `fish_preexec` hook instead of `fish_prompt` (#512) * Use `fish_postexec` to make sure direnv hook executed 'after' the directory has changed when using `cd`. * improve zsh hook (#514) ## config.toml * rename the configuration from config.toml to direnv.toml (#498) * add warn_timeout option. DIRENV_WARN_TIMEOUT is now deprecated. ## stdlib * `direnv_load` can now handle stdout outputs * stdlib: add layout_julia * Handle failing pipenv on empty file and avoid an extra pipenv execution (#510) * fix `source_env` behaviour when the file doesn't exists (#487) * `watch_file` can now watch multiple files in a single invocation (#524) * `layout_python`: prefer venv over virtualenv. Do not export VIRUAL_ENV if $python_version is unavailable or a virtual environment does not exist/can't be created * Adds layout_pyenv (#505) * Fix `source_up` docs to explain that search starts in parent directory (#518) * fix `path_add` to not leak local variables * `layout_pyenv`: support multiple python versions (#525) * Add a `direnv_version ` command to check the direnv version. * `dotenv`: handle undefined variables * source files from `.config/direnv/lib/*.sh` * stdlib: set `-euo pipefail` 2.20.1 / 2019-03-31 ================== * ci: try to fix releases 2.20.0 / 2019-03-31 ================== * CHANGE: Use source instead of eval on fish hook * DOC: Remove duplicate build badge (#465) * DOC: add note about auth (#463) * DOC: change nixos link (#460) * FIX: Corrects reverse patching when using exec cmd. (#466) * FIX: Perform stricter search for existing Anaconda environments (#462) * FIX: arity mismatch for elvish (#482) * FIX: avoid reloading on each prompt after error (#468) * FIX: improve bash hook handlign of empty PROMPT_COMMAND (#473) * FIX: improved the tests for bash, zsh, fish and tcsh (#469) * MISC: migrated from Travis CI to Azure Pipelines (#484) 2.19.2 / 2019-02-09 ================== * FIX: file_times: check Stat and Lstat (#457) 2.19.1 / 2019-01-31 ================== * FIX: watched files now handle symlinks properly. Thanks @grahamc! #452 2.19.0 / 2019-01-11 ================== * NEW: add support for .env variable expansion. Thanks to @hakamadare! 2.18.2 / 2018-11-23 ================== * make: generate direnv.exe on windows (#417) 2.18.1 / 2018-11-22 ================== * travis: fix the release process 2.18.0 / 2018-11-22 ================== A lot of changes! * stdlib: add DIRENV_IN_ENVRC (#414) * Fix typo in readme. (#412) * Merge pull request #407 from zimbatm/direnv-dump-shell * direnv dump can now dump to arbitrary shells * add a new "gzenv" shell * move gzenv into new package * shell: introduce a dump capability * cleanup the shells * Add alias '--version' to version command. Closes #377. (#404) * Correctes spelling of openSUSE (#403) * testing: elvish 0.12 is released now (#402) * Merge pull request #397 from zimbatm/readme-packaging-status * README: add packaging status badge * README: remove equinox installation * direnv show_dump: new command to debug encoded env (#395) * Document possibility to unset vars (#392) * stdlib: fix typo * go dep: update Gopkg.lock * make: don't make shfmt a dependency * Avoid to add unnecessary trailing semicolon character (#384) * add asdf to the list of known projects * stdlib.go: re-generate * Add PHP layout to stdlib (#346) * make: fix formatting * README: add build status badge * Overhaul the build system (#375) * stdlib, layout_pipenv: handle `$PIPENV_PIPFILE` (#371) * README: improve the source build instructions 2.17.0 / 2018-06-17 ================== * CHANGE: hook expands the direnv path. Ensures that direnv can be executed even if the PATH is changed #369. * CHANGE: stdlib: direnv_load: disallow watching in child Allows the `use nix --pure` scenario in #368 * README: add OpenSuSE to the list of distros * Revert "use_nix: unset IN_NIX_SHELL" 2.16.0 / 2018-05-09 ================== * NEW: add support for elvish (#356) * NEW: config: allow to disable stdin on eval (#351) * DOC: Add the usage of source_up to the README (#347) * FIX: default.nix: fix compilation 2.15.2 / 2018-02-25 ================== * FIX: lintian warnings (#340) * FIX: release process (#342) 2.15.1 / 2018-02-24 ================== * FIX: support for go 1.10 (#339) 2.15.0 / 2018-02-23 ================== * NEW: TOML configuration file! (#332, #337) * NEW: support for allow folder whitelist (#332) * NEW: add anaconda support (#312) * CHANGE: use_nix: unset IN_NIX_SHELL 2.14.0 / 2017-12-13 ================== * NEW: Add support for Pipenv layout (#314) * CHANGE: direnv version: make public * FIX: direnv edit: run the command through bash * FIX: website: update ditto to v0.15 2.13.3 / 2017-11-30 ================== * FIX: fixes dotenv loading issue on macOS `''=''` 2.13.2 / 2017-11-28 ================== * FIX: direnv edit: fix path escaping * FIX: stdlib: fix find_up * FIX: stdlib: use absolute path in source_up * FIX: remove ruby as a build dependency * FIX: go-dotenv: update to latest master to fix a parsing error 2.13.1 / 2017-09-27 ================== * FIX: stdlib: make direnv_layout_dir lazy (#298) 2.13.0 / 2017-09-24 ================== * NEW: stdlib: configurable direnv_layout_dir * CHANGE: stdlib: source the direnvrc directly * FIX: permit empty NODE_VERSION_PREFIX variable * FIX: pwd: Don't use -P to remove symlinks (#295) * FIX: also reload when mtime goes back in time * FIX: Prevent `$HOME` path from being striked (#287) * BUILD: use the new `dep` tool to manage dependencies * BUILD: dotenv: move to vendor folder 2.12.2 / 2017-07-05 ================== * stdlib layout_python: fixes on no arg 2.12.1 / 2017-07-01 ================== * FIX: stdlib path_add(), see #278 * FIX: install from source instructions 2.12.0 / 2017-06-30 ================== * NEW: support multiple items in path_add and PATH_add (#276) * NEW: add a configurable DIRENV_WARN_TIMEOUT option (#273) * CHANGE: rewrite the dotenv parsing, now supports commented lines * CHANGE: pass additional args to virtualenv (#261) * FIX: stdlib watch_file(): escaping fix * FIX: only output color if $TERM is not dumb (#264) * FIX: the watch_file documentation 2.11.3 / 2017-03-02 ================== * FIX: node version sorting (#255) 2.11.2 / 2017-03-01 ================== * FIX: Typo in MANPATH_add always generates "PATH missing" error. (#256) 2.11.1 / 2017-02-20 ================== * FIX: only deploy the go 1.8 version 2.11.0 / 2017-02-20 ================== * NEW: stdlib.sh: introduce MANPATH_add (#248) * NEW: provide packages using the equinox service * CHANGE: test direnv with go 1.8 (#254) * FIX: Add warning about source_env/up * FIX: go-md2man install instruction 2.10.0 / 2016-12-10 ================== * NEW: `use guix` (#242) * CHANGE: use go-md2man to generate the man pages * FIX: tcsh escaping (#241) * FIX: doc typos and rewords (#226) 2.9.0 / 2016-07-03 ================== * NEW: use_nix() is now watching default.nix and shell.nix * NEW: Allow to fix the bash path at built time * FIX: Panic on `direnv current` with no argument * FIX: Permit empty NODE_VERSION_PREFIX variable * FIX: layout_python: fail properly when python is not found 2.8.1 / 2016-04-04 ================== * FIX: travis dist release 2.8.0 / 2016-03-27 ================== * NEW: `direnv export json` to facilitate IDE integration * NEW: watch functionality thanks to @avnik Now direnv also reload on associate .env and .envrc changes. * NEW: stdlib `watch_file` function thanks to @avnik Allows to monitor more files for change. * NEW: stdlib `use node` function thanks to @wilmoore * NEW: `direnv prune` to remove old allowed files thanks to @punitagrawal Only works with newly-generated files since we're not storing the path inside of them. 2.7.0 / 2015-08-08 ================== * NEW: use_nix() helper to stdlib. Thanks @gfxmonk * FIX: Added SHELLOPTS to ignored vars. Thanks @fernandomora * FIX: Removed shellcheck offenses in the stdlib, better escaping * FIX: typos. Thanks @camelpunch, @oppegard 2.6.1 / 2015-06-23 ================== * FIX: source_env handles missing .envrc gracefully. Thanks @gerhard * FIX: Empty variable as unloading in Vim. Thanks @p0deje * FIX: Corrected spelling mistake in deny command. Thanks @neanias 2.6.0 / 2015-02-15 ================== * NEW: tcsh is now supported ! Thanks @bbense * CHANGE: `direnv dump` now ignores `BASH_FUNC_` exports. Thanks @gfxmonk * CHANGE: Interactive input during load is now possible. Thanks @toao * FIX: allow workaround for tmux users: `alias tmux='direnv exec / tmux'` * FIX: hardened fish shell escaping thanks to @gfxmonk Thanks @bbense @vially and @dadooda for corrections in the docs 2.5.0 / 2014-11-04 ================== * NEW: Use a different virtualenv per python versions for easier version switching. Eg: ./.direnv/python-${python_version} * NEW: Makes `layout python3` a shortcut for `layout python python3`. Thanks @ghickman ! * NEW: Allows to specify which executable of python to use in `layout_python` * CHANGE: `layout python` now unsets $PYTHONHOME to better mimic virtualenv * CHANGE: Don't make virtualenvs relocatable. Fixes #137 * OTHER: Use Travis to push release builds to github 2.4.0 / 2014-06-15 ================== * NEW: Try to detect an editor in the PATH if EDITOR is not set. * NEW: Preliminary support for vim * NEW: New site: put the doc inside the project so it stays in sync * NEW: Support for Cygwin - Thanks @CMCDragonkai ! * NEW: Allow to disable logging by setting an empty `DIRENV_LOG_FORMAT` * NEW: stdlib `layout perl`. Thanks @halkeye ! * CHANGE: layout ruby: share the gem home starting from rubygems v2.2.0 * CHANGE: Allow arbitrary number of args in `log_status` * CHANGE: Bump command timeout to 5 seconds * FIX: Adds selected bash executable in `direnv status` * FIX: man changes, replaced abandonned ronn by md2man * FIX: `make install` was creating a ./bin directory * FIX: issue #114 - work for blank envs. Thanks @pwaller ! * FIX: man pages warning. Thanks @punitagrawal ! * FIX: Multi-arg EDITOR was broken #108 * FIX: typos in doc. Thanks @HeroicEric and @lmarlow ! * FIX: If two paths don't have a common ancestors, don't make them relative. * FIX: missing doc on layered .envrc. Thanks @take ! 2.3.0 / 2014-02-06 ================== * NEW: DIRENV_LOG_FORMAT environment variable can be used tocontrol log formatting * NEW: `direnv exec [DIR] ` to execute programs with an .envrc context * CHANGE: layout_python now tries to make your virtualenv relocatable * CHANGE: the export diff is not from the old env, not the current env * CHANGE: layout_go now also adds $PWD/bin in the PATH * FIX: Hides the DIRENV_ variables in the output diff. Fixes #94 * FIX: Makes sure the path used in the allow hash is absolute. See #95 * FIX: Set the executable bit on direnv on install * FIX: Some bash installs had a parse error in the hook. 2.2.1 / 2014-01-12 ================== The last release was heavily broken. Ooops ! * FIX: Refactored the whole export and diff mechanism. Fixes #92 regression. * CHANGE: DIRENV_BACKUP has been renamed to DIRENV_DIFF 2.2.0 / 2014-01-11 ================== Restart your shells on upgrade, the format of DIRENV_BACKUP has changed and is incompatible with previous versions. * NEW: `direnv_load ` stdlib function * CHANGE: Only backup the diff of environments. Fixes #82 * CHANGE: Renames `$DIRENV_PATH` to `$direnv` in the stdlib. * CHANGE: Allow/Deny mechanism now includes the path to make it more secure. * CHANGE: `direnv --help` is an alias to `direnv help` * CHANGE: more consistent log outputs and error messages * CHANGE: `direnv edit` only auto-allows the .envrc if it's mtime has changed. * CHANGE: Fixes old bash (OSX) segfault in some cases. See #81 * CHANGE: The stdlib `dotenv` now supports more .env syntax * FIX: Restore the environment properly after loading errors. 2.1.0 / 2013-11-10 ================== * Added support for the fish shell. See README.md for install instructions. * Stop recommending using $0 to detect the shell. Fixes #64. * Makes the zsh hook resistant to double-hooking. * Makes the bash hook resistant to double-hooking. * More precise direnv allow error message. Fixes #72 2.0.1 / 2013-07-27 ================== * Fixes shell detection corner case 2.0.0 / 2013-06-16 ================== When upgrading from direnv 1.x make sure to restart your shell. The rest is relatively backward-compatible. * changed the execution model. Everything is in a single static executable * most of the logic has been rewritten in Go * robust shell escaping (supports UTF-8 in env vars) * robust eval/export loop, avoids retrys on every prompt if there is an error * stdlib: added the `dotenv [PATH]` command to load .env files * command: added `direnv reload` to force-reload your environment direnv-2.21.2/CNAME000066400000000000000000000000131361411000000136070ustar00rootroot00000000000000direnv.net direnv-2.21.2/GNUmakefile000066400000000000000000000073701361411000000151300ustar00rootroot00000000000000############################################################################ # Variables ############################################################################ # Set this to change the target installation path DESTDIR ?= /usr/local BINDIR = ${DESTDIR}/bin MANDIR = ${DESTDIR}/share/man DISTDIR ?= dist # filename of the executable exe = direnv$(shell go env GOEXE) # Override the go executable GO = go # Change if you want to fork direnv PACKAGE = github.com/direnv/direnv # BASH_PATH can also be passed to hard-code the path to bash at build time SHELL = bash ############################################################################ # Common ############################################################################ .PHONY: all all: build man export GO111MODULE=on export GOFLAGS=-mod=vendor ############################################################################ # Build ############################################################################ .PHONY: build build: direnv .PHONY: clean clean: rm -rf \ .gopath \ direnv GO_LDFLAGS = ifeq ($(shell uname), Darwin) # Fixes DYLD_INSERT_LIBRARIES issues # See https://github.com/direnv/direnv/issues/194 GO_LDFLAGS += -linkmode=external endif ifdef BASH_PATH GO_LDFLAGS += -X main.bashPath=$(BASH_PATH) endif ifneq ($(strip $(GO_LDFLAGS)),) GO_BUILD_FLAGS = -ldflags '$(GO_LDFLAGS)' endif direnv: stdlib.go *.go $(GO) build $(GO_BUILD_FLAGS) -o $(exe) stdlib.go: stdlib.sh cat $< | ./script/str2go main StdLib $< > $@ version.go: version.txt echo package main > $@ echo >> $@ echo "// Version is direnv's version" echo 'const Version = "$(shell cat $<)"' >> $@ ############################################################################ # Format all the things ############################################################################ .PHONY: fmt fmt-go fmt-sh fmt: fmt-go fmt-sh fmt-go: $(GO) fmt fmt-sh: @command -v shfmt >/dev/null || (echo "Could not format stdlib.sh because shfmt is missing. Run: go get -u mvdan.cc/sh/cmd/shfmt"; false) shfmt -i 2 -w stdlib.sh ############################################################################ # Documentation ############################################################################ man_md = $(wildcard man/*.md) roffs = $(man_md:.md=) .PHONY: man man: $(roffs) %.1: %.1.md @command -v go-md2man >/dev/null || (echo "Could not generate man page because go-md2man is missing. Run: go get -u github.com/cpuguy83/go-md2man"; false) go-md2man -in $< -out $@ ############################################################################ # Testing ############################################################################ tests = \ test-shellcheck \ test-stdlib \ test-go \ test-go-lint \ test-go-fmt \ test-bash \ test-elvish \ test-fish \ test-tcsh \ test-zsh .PHONY: $(tests) test: build $(tests) @echo @echo SUCCESS! test-shellcheck: shellcheck stdlib.sh test-stdlib: build ./test/stdlib.bash test-go: $(GO) test -v ./... test-go-lint: golangci-lint run test-bash: bash ./test/direnv-test.bash # Needs elvish 0.12+ test-elvish: elvish ./test/direnv-test.elv test-fish: fish ./test/direnv-test.fish test-tcsh: tcsh -e ./test/direnv-test.tcsh test-zsh: zsh ./test/direnv-test.zsh ############################################################################ # Installation ############################################################################ .PHONY: install install: all install -d $(BINDIR) install $(exe) $(BINDIR) install -d $(MANDIR)/man1 cp -R man/*.1 $(MANDIR)/man1 .PHONY: dist dist: @command -v gox >/dev/null || (echo "Could not generate dist because gox is missing. Run: go get -u github.com/mitchellh/gox"; false) gox -output "$(DISTDIR)/direnv.{{.OS}}-{{.Arch}}" direnv-2.21.2/LICENSE000066400000000000000000000020711361411000000140540ustar00rootroot00000000000000MIT License Copyright (c) 2019 zimbatm and contributors 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. direnv-2.21.2/README.md000066400000000000000000000142241361411000000143310ustar00rootroot00000000000000direnv -- unclutter your .profile ================================= [![Built with Nix](https://builtwithnix.org/badge.svg)](https://builtwithnix.org) [![Build Status](https://dev.azure.com/direnv/direnv/_apis/build/status/direnv.direnv?branchName=master)](https://dev.azure.com/direnv/direnv/_build/latest?definitionId=1&branchName=master) [![Packaging status](https://repology.org/badge/tiny-repos/direnv.svg)](https://repology.org/project/direnv/versions) [![latest packaged version(s)](https://repology.org/badge/latest-versions/direnv.svg)](https://repology.org/project/direnv/versions) `direnv` is an extension for your shell. It augments existing shells with a new feature that can load and unload environment variables depending on the current directory. ## Use cases * Load 12factor apps environment variables * Create per-project isolated development environments * Load secrets for deployment ## How it works Before each prompt, direnv checks for the existence of a `.envrc` file in the current and parent directories. If the file exists (and is authorized), it is loaded into a **bash** sub-shell and all exported variables are then captured by direnv and then made available to the current shell. It supports hooks for all the common shells like bash, zsh, tcsh and fish. This allows project-specific environment variables without cluttering the `~/.profile` file. Because direnv is compiled into a single static executable, it is fast enough to be unnoticeable on each prompt. It is also language-agnostic and can be used to build solutions similar to rbenv, pyenv and phpenv. ## Getting Started ### Prerequisites * Unix-like operating system (macOS, Linux, ...) * A supported shell (bash, zsh, tcsh, fish, elvish) ### Basic Installation 1. direnv is packaged in most distributions already. See [the installation documentation](docs/installation.md) for details. 2. [hook direnv into your shell](docs/hook.md). Now restart your shell. ### Quick demo To follow along in your shell once direnv is installed. ``` # Create a new folder for demo purposes. $ mkdir ~/my-project $ cd ~/my-project # Show that the FOO environment variable is not loaded. $ echo ${FOO-nope} nope # Create a new .envrc. This file is bash code that is going to be loaded by # direnv. $ echo export FOO=foo > .envrc .envrc is not allowed # The security mechanism didn't allow to load the .envrc. Since we trust it, # let's allow it's execution. $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +FOO # Show that the FOO environment variable is loaded. $ echo ${FOO-nope} foo # Exit the project $ cd .. direnv: unloading # And now FOO is unset again $ echo ${FOO-nope} nope ``` ### The stdlib Exporting variables by hand is a bit repetitive so direnv provides a set of utility functions that are made available in the context of the `.envrc` file. As an example, the `PATH_add` function is used to expand and prepend a path to the $PATH environment variable. Instead of `export PATH=$PWD/bin:$PATH` you can write `PATH_add bin`. It's shorter and avoids a common mistake where `$PATH=bin`. To find the documentation for all available functions check the [direnv-stdlib(1) man page](man/direnv-stdlib.1.md). It's also possible to create your own extensions by creating a bash file at `~/.config/direnv/direnvrc` or `~/.config/direnv/lib/*.sh`. This file is loaded before your `.envrc` and thus allows you to make your own extensions to direnv. ## Docs * [Install direnv](docs/installation.md) * [Hook into your shell](docs/hook.md) * [Develop for direnv](docs/development.md) * [Manage your rubies with direnv and ruby-install](docs/ruby.md) * [Community Wiki](https://github.com/direnv/direnv/wiki) Make sure to take a look at the wiki! It contains all sorts of useful information such as common recipes, editor integration, tips-and-tricks. ### Man pages * [direnv(1) man page](man/direnv.1.md) * [direnv-stdlib(1) man page](man/direnv-stdlib.1.md) * [direnv.toml(1) man page](man/direnv.toml.1.md) ### FAQ Based on GitHub issues interactions, here are the top things that have been confusing for users: 1. direnv has a standard library of functions, a collection of utilities that I found useful to have and accumulated over the years. You can find it here: https://github.com/direnv/direnv/blob/master/stdlib.sh 2. It's possible to override the stdlib with your own set of function by adding a bash file to `~/.config/direnv/direnvrc`. This file is loaded and it's content made available to any `.envrc` file. 3. direnv is not loading the `.envrc` into the current shell. It's creating a new bash sub-process to load the stdlib, direnvrc and `.envrc`, and only exports the environment diff back to the original shell. This allows direnv to record the environment changes accurately and also work with all sorts of shells. It also means that aliases and functions are not exportable right now. ## Contributing Bug reports, contributions and forks are welcome. All bugs or other forms of discussion happen on http://github.com/direnv/direnv/issues . Or drop by on [IRC (#direnv on freenode)](irc://irc.freenode.net/#direnv) to have a chat. If you ask a question make sure to stay around as not everyone is active all day. ## Similar projects Here is a list of other projects found in the same design space. Feel free to submit new ones. * [Environment Modules](http://modules.sourceforge.net/) - one of the oldest (in a good way) environment-loading systems * [autoenv](https://github.com/kennethreitz/autoenv) - lightweight; doesn't support unloads * [zsh-autoenv](https://github.com/Tarrasch/zsh-autoenv) - a feature-rich mixture of autoenv and [smartcd](https://github.com/cxreg/smartcd): enter/leave events, nesting, stashing (Zsh-only). * [asdf](https://github.com/asdf-vm/asdf) - a pure bash solution that has a plugin system. The [asdf-direnv](https://github.com/asdf-community/asdf-direnv) plugin allows using asdf managed tools with direnv. * [ondir](https://github.com/alecthomas/ondir) - OnDir is a small program to automate tasks specific to certain directories ## COPYRIGHT [MIT licence](LICENSE) - Copyright (C) 2019 @zimbatm and [contributors](https://github.com/direnv/direnv/graphs/contributors) direnv-2.21.2/_config.yml000066400000000000000000000011371361411000000152000ustar00rootroot00000000000000theme: jekyll-theme-primer title: direnv description: "unclutter your .profile" url: "https://direnv.net" exclude: - test - vendor # see https://github.com/github/pages-gem/blob/754a725e4766d4329bb1dd0e07c638a045ad2c04/lib/github-pages/plugins.rb#L6-L42 plugins: - jemoji - jekyll-avatar - jekyll-default-layout - jekyll-feed - jekyll-mentions - jekyll-readme-index - jekyll-sitemap markdown: CommonMarkGhPages # see https://github.com/gjtorikian/commonmarker#parse-options commonmark: options: - FOOTNOTES - SMART extensions: - autolink - strikethrough - table direnv-2.21.2/cmd_allow.go000066400000000000000000000030421361411000000153360ustar00rootroot00000000000000package main import ( "fmt" "os" "path/filepath" ) // CmdAllow is `direnv allow [PATH_TO_RC]` var CmdAllow = &Cmd{ Name: "allow", Desc: "Grants direnv to load the given .envrc", Args: []string{"[PATH_TO_RC]"}, Action: actionWithConfig(cmdAllowAction), } var migrationMessage = ` Migrating the allow data to the new location The allowed .envrc permissions used to be stored in the XDG_CONFIG_DIR. It's better to keep that folder for user-editable configuration so the data is being moved to XDG_DATA_HOME. ` func cmdAllowAction(env Env, args []string, config *Config) (err error) { var rcPath string if len(args) > 1 { rcPath = args[1] } else { if rcPath, err = os.Getwd(); err != nil { return err } } if _, err = os.Stat(config.AllowDir()); os.IsNotExist(err) { oldAllowDir := filepath.Join(config.ConfDir, "allow") if _, err = os.Stat(oldAllowDir); err == nil { fmt.Println(migrationMessage) fmt.Printf("moving %s to %s\n", oldAllowDir, config.AllowDir()) if err = os.MkdirAll(filepath.Dir(config.AllowDir()), 0755); err != nil { return err } if err = os.Rename(oldAllowDir, config.AllowDir()); err != nil { return err } fmt.Printf("creating a symlink back from %s to %s for back-compat.\n", config.AllowDir(), oldAllowDir) if err = os.Symlink(config.AllowDir(), oldAllowDir); err != nil { return err } fmt.Println("") fmt.Println("All done, have a nice day!") } } rc := FindRC(rcPath, config) if rc == nil { return fmt.Errorf(".envrc file not found") } return rc.Allow() } direnv-2.21.2/cmd_apply_dump.go000066400000000000000000000015511361411000000163750ustar00rootroot00000000000000package main import ( "fmt" "io/ioutil" ) // CmdApplyDump is `direnv apply_dump FILE` var CmdApplyDump = &Cmd{ Name: "apply_dump", Desc: "Accepts a filename containing `direnv dump` output and generates a series of bash export statements to apply the given env", Args: []string{"FILE"}, Private: true, Action: actionSimple(cmdApplyDumpAction), } func cmdApplyDumpAction(env Env, args []string) (err error) { if len(args) < 2 { return fmt.Errorf("not enough arguments") } if len(args) > 2 { return fmt.Errorf("too many arguments") } filename := args[1] dumped, err := ioutil.ReadFile(filename) if err != nil { return err } dumpedEnv, err := LoadEnv(string(dumped)) if err != nil { return err } diff := env.Diff(dumpedEnv) exports := diff.ToShell(Bash) _, err = fmt.Println(exports) if err != nil { return err } return } direnv-2.21.2/cmd_current.go000066400000000000000000000011601361411000000157010ustar00rootroot00000000000000package main import ( "errors" ) // CmdCurrent is `direnv current` var CmdCurrent = &Cmd{ Name: "current", Desc: "Reports whether direnv's view of a file is current (or stale)", Args: []string{"PATH"}, Private: true, Action: actionSimple(cmdCurrentAction), } func cmdCurrentAction(env Env, args []string) (err error) { if len(args) < 2 { err = errors.New("missing PATH argument") return } path := args[1] watches := NewFileTimes() watchString, ok := env[DIRENV_WATCHES] if ok { err = watches.Unmarshal(watchString) if err != nil { return } } err = watches.CheckOne(path) return } direnv-2.21.2/cmd_deny.go000066400000000000000000000010731361411000000151610ustar00rootroot00000000000000package main import ( "fmt" "os" ) // CmdDeny is `direnv deny [PATH_TO_RC]` var CmdDeny = &Cmd{ Name: "deny", Desc: "Revokes the authorization of a given .envrc", Args: []string{"[PATH_TO_RC]"}, Action: actionWithConfig(cmdDenyAction), } func cmdDenyAction(env Env, args []string, config *Config) (err error) { var rcPath string if len(args) > 1 { rcPath = args[1] } else { if rcPath, err = os.Getwd(); err != nil { return } } rc := FindRC(rcPath, config) if rc == nil { return fmt.Errorf(".envrc file not found") } return rc.Deny() } direnv-2.21.2/cmd_dotenv.go000066400000000000000000000020001361411000000155100ustar00rootroot00000000000000package main import ( "fmt" "io/ioutil" "github.com/direnv/go-dotenv" ) // CmdDotEnv is `direnv dotenv [SHELL [PATH_TO_DOTENV]]` // Transforms a .env file to evaluatable `export KEY=PAIR` statements. // // See: https://github.com/bkeepers/dotenv and // https://github.com/ddollar/foreman var CmdDotEnv = &Cmd{ Name: "dotenv", Desc: "Transforms a .env file to evaluatable `export KEY=PAIR` statements", Args: []string{"[SHELL]", "[PATH_TO_DOTENV]"}, Private: true, Action: actionSimple(cmdDotEnvAction), } func cmdDotEnvAction(env Env, args []string) (err error) { var shell Shell var newenv Env var target string if len(args) > 1 { shell = DetectShell(args[1]) } else { shell = Bash } if len(args) > 2 { target = args[2] } if target == "" { target = ".env" } var data []byte if data, err = ioutil.ReadFile(target); err != nil { return } newenv, err = dotenv.Parse(string(data)) if err != nil { return err } str := newenv.ToShell(shell) fmt.Println(str) return } direnv-2.21.2/cmd_dump.go000066400000000000000000000016541361411000000151740ustar00rootroot00000000000000package main import ( "fmt" "os" "strconv" ) // CmdDump is `direnv dump` var CmdDump = &Cmd{ Name: "dump", Desc: "Used to export the inner bash state at the end of execution", Args: []string{"[SHELL]", "[FILE]"}, Private: true, Action: actionSimple(cmdDumpAction), } func cmdDumpAction(env Env, args []string) (err error) { target := "gzenv" w := os.Stdout if len(args) > 1 { target = args[1] } var filePath string if len(args) > 2 { filePath = args[2] } else { filePath = os.Getenv("DIRENV_DUMP_FILE_PATH") } if filePath != "" { if num, err := strconv.Atoi(filePath); err == nil { w = os.NewFile(uintptr(num), filePath) } else { w, err = os.OpenFile(filePath, os.O_WRONLY, 0666) if err != nil { return err } } } shell := DetectShell(target) if shell == nil { return fmt.Errorf("unknown target shell '%s'", target) } _, err = fmt.Fprintln(w, shell.Dump(env)) return } direnv-2.21.2/cmd_edit.go000066400000000000000000000040721361411000000151510ustar00rootroot00000000000000package main import ( "fmt" "log" "os" "os/exec" "path/filepath" "strings" ) // CmdEdit is `direnv edit [PATH_TO_RC]` var CmdEdit = &Cmd{ Name: "edit", Desc: `Opens PATH_TO_RC or the current .envrc into an $EDITOR and allow the file to be loaded afterwards.`, Args: []string{"[PATH_TO_RC]"}, Action: actionWithConfig(cmdEditAction), } func cmdEditAction(env Env, args []string, config *Config) (err error) { var rcPath string var times *FileTimes var foundRC *RC defer log.SetPrefix(log.Prefix()) log.SetPrefix(log.Prefix() + "cmd_edit: ") foundRC = config.FindRC() if foundRC != nil { times = &foundRC.times } if len(args) > 1 { rcPath = args[1] fi, _ := os.Stat(rcPath) if fi != nil && fi.IsDir() { rcPath = filepath.Join(rcPath, ".envrc") } } else { if foundRC == nil { return fmt.Errorf(".envrc not found. Use `direnv edit .` to create a new envrc in the current directory") } rcPath = foundRC.path } editor := env["EDITOR"] if editor == "" { logError("$EDITOR not found.") editor = detectEditor(env["PATH"]) if editor == "" { err = fmt.Errorf("could not find a default editor in the PATH") return } } run := fmt.Sprintf("%s %s", editor, BashEscape(rcPath)) cmd := exec.Command(config.BashPath, "-c", run) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err = cmd.Run(); err != nil { return } foundRC = FindRC(rcPath, config) logDebug("foundRC: %#v", foundRC) logDebug("times: %#v", times) if times != nil { logDebug("times.Check(): %#v", times.Check()) } if foundRC != nil && (times == nil || times.Check() != nil) { err = foundRC.Allow() } return } // Utils // Editors contains a list of known editors and how to start them. var Editors = [][]string{ {"subl", "-w"}, {"mate", "-w"}, {"open", "-t", "-W"}, // Opens with the default text editor on mac {"nano"}, {"vim"}, {"emacs"}, } func detectEditor(pathenv string) string { for _, editor := range Editors { if _, err := lookPath(editor[0], pathenv); err == nil { return strings.Join(editor, " ") } } return "" } direnv-2.21.2/cmd_exec.go000066400000000000000000000027341361411000000151530ustar00rootroot00000000000000package main import ( "context" "fmt" "os" "path/filepath" "syscall" ) // CmdExec is `direnv exec DIR ...` var CmdExec = &Cmd{ Name: "exec", Desc: "Executes a command after loading the first .envrc found in DIR", Args: []string{"DIR", "COMMAND", "[...ARGS]"}, Action: actionWithConfig(cmdExecAction), } func cmdExecAction(env Env, args []string, config *Config) (err error) { var ( backupDiff *EnvDiff newEnv Env rcPath string command string ) if len(args) < 2 { return fmt.Errorf("missing DIR and COMMAND arguments") } rcPath = filepath.Clean(args[1]) fi, err := os.Stat(rcPath) if err != nil { return } if fi.IsDir() { if len(args) < 3 { return fmt.Errorf("missing COMMAND argument") } command = args[2] args = args[2:] } else { command = rcPath rcPath = filepath.Dir(rcPath) args = args[1:] } rc := FindRC(rcPath, config) // Restore pristine environment if needed if backupDiff, err = config.EnvDiff(); err == nil && backupDiff != nil { env = backupDiff.Reverse().Patch(env) } env.CleanContext() // Load the rc if rc != nil { if newEnv, err = rc.Load(context.Background(), config, env); err != nil { return } } else { newEnv = env } var commandPath string commandPath, err = lookPath(command, newEnv["PATH"]) if err != nil { err = fmt.Errorf("command '%s' not found on PATH '%s'", command, newEnv["PATH"]) return } err = syscall.Exec(commandPath, args, newEnv.ToGoEnv()) return } direnv-2.21.2/cmd_expand_path.go000066400000000000000000000020201361411000000165060ustar00rootroot00000000000000package main import ( "flag" "fmt" "os" "path/filepath" ) func expandPath(path, relTo string) string { if filepath.IsAbs(path) { return path } return filepath.Clean(filepath.Join(relTo, path)) } // CmdExpandPath is `direnv expand_path PATH [REL_TO]` var CmdExpandPath = &Cmd{ Name: "expand_path", Desc: "Transforms a PATH to an absolute path to REL_TO or $PWD", Args: []string{"PATH", "[REL_TO]"}, Private: true, Action: actionSimple(cmdExpandPathAction), } func cmdExpandPathAction(env Env, args []string) (err error) { var path string flagset := flag.NewFlagSet(args[0], flag.ExitOnError) err = flagset.Parse(args[1:]) if err != nil { return err } path = flagset.Arg(0) if path == "" { return fmt.Errorf("PATH missing") } if !filepath.IsAbs(path) { wd, err := os.Getwd() if err != nil { return err } relTo := flagset.Arg(1) if relTo == "" { relTo = wd } else { relTo = expandPath(relTo, wd) } path = expandPath(path, relTo) } _, err = fmt.Println(path) return } direnv-2.21.2/cmd_export.go000066400000000000000000000076141361411000000155520ustar00rootroot00000000000000package main import ( "context" "fmt" "log" "os" "os/signal" "sort" "strings" ) // CmdExport is `direnv export $0` var CmdExport = &Cmd{ Name: "export", Desc: "loads an .envrc and prints the diff in terms of exports", Args: []string{"SHELL"}, Private: true, Action: cmdWithWarnTimeout(actionWithConfig(actionWithCancel(exportCommand))), } func actionWithCancel(fn func(ctx context.Context, env Env, args []string, config *Config) error) actionWithConfig { return func(env Env, args []string, config *Config) error { ctx, cancel := context.WithCancel(context.Background()) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) // cancel the context on Ctrl-C go func() { <-c cancel() }() return fn(ctx, env, args, config) } } func exportCommand(ctx context.Context, env Env, args []string, config *Config) (err error) { defer log.SetPrefix(log.Prefix()) log.SetPrefix(log.Prefix() + "export:") logDebug("start") ec := ExportContext{ env: env, config: config, } var target string if len(args) > 1 { target = args[1] } shell := DetectShell(target) if shell == nil { return fmt.Errorf("unknown target shell '%s'", target) } logDebug("loading RCs") if ec.getRCs(); !ec.hasRC() { return nil } logDebug("updating RC") if err = ec.updateRC(ctx); err != nil { logDebug("err: %v", err) } if ec.newEnv == nil { logDebug("newEnv nil, exiting") return } diffString := ec.diffString(shell) logDebug("env diff %s", diffString) fmt.Print(diffString) return } // ExportContext is a sort of state holder struct that is being used to // record changes before the export finishes. type ExportContext struct { config *Config foundRC *RC loadedRC *RC env Env oldEnv Env newEnv Env } func (ec *ExportContext) getRCs() { ec.loadedRC = ec.config.LoadedRC() ec.foundRC = ec.config.FindRC() } func (ec *ExportContext) hasRC() bool { return ec.foundRC != nil || ec.loadedRC != nil } func (ec *ExportContext) updateRC(ctx context.Context) (err error) { defer log.SetPrefix(log.Prefix()) log.SetPrefix(log.Prefix() + "update:") ec.oldEnv = ec.env.Copy() var backupDiff *EnvDiff if backupDiff, err = ec.config.EnvDiff(); err == nil && backupDiff != nil { ec.oldEnv = backupDiff.Reverse().Patch(ec.env) } if err != nil { err = fmt.Errorf("EnvDiff() failed: %q", err) return } logDebug("Determining action:") logDebug("foundRC: %#v", ec.foundRC) logDebug("loadedRC: %#v", ec.loadedRC) switch { case ec.foundRC == nil: logDebug("no RC found, unloading") ec.unloadEnv() case ec.loadedRC == nil: logDebug("no RC (implies no DIRENV_DIFF),loading") err = ec.loadRC(ctx) case ec.loadedRC.path != ec.foundRC.path: logDebug("new RC, loading") err = ec.loadRC(ctx) case ec.loadedRC.times.Check() != nil: logDebug("file changed, reloading") err = ec.loadRC(ctx) default: logDebug("no update needed") } return } func (ec *ExportContext) loadRC(ctx context.Context) (err error) { ec.newEnv, err = ec.foundRC.Load(ctx, ec.config, ec.oldEnv) return } func (ec *ExportContext) unloadEnv() { logStatus(ec.env, "unloading") ec.newEnv = ec.oldEnv.Copy() cleanEnv(ec.newEnv) } func cleanEnv(env Env) { env.CleanContext() } func (ec *ExportContext) diffString(shell Shell) string { oldDiff := ec.oldEnv.Diff(ec.newEnv) if oldDiff.Any() { var out []string for key := range oldDiff.Prev { _, ok := oldDiff.Next[key] if !ok && !direnvKey(key) { out = append(out, "-"+key) } } for key := range oldDiff.Next { _, ok := oldDiff.Prev[key] if direnvKey(key) { continue } if ok { out = append(out, "~"+key) } else { out = append(out, "+"+key) } } sort.Strings(out) if len(out) > 0 { logStatus(ec.env, "export %s", strings.Join(out, " ")) } } diff := ec.env.Diff(ec.newEnv) return diff.ToShell(shell) } func direnvKey(key string) bool { return strings.HasPrefix(key, "DIRENV_") } direnv-2.21.2/cmd_help.go000066400000000000000000000014461361411000000151560ustar00rootroot00000000000000package main import ( "fmt" "strings" ) // CmdHelp is `direnv help` var CmdHelp = &Cmd{ Name: "help", Desc: "shows this help", Args: []string{"[SHOW_PRIVATE]"}, Aliases: []string{"--help"}, Action: actionSimple(func(env Env, args []string) (err error) { var showPrivate = len(args) > 1 fmt.Printf(`direnv v%s Usage: direnv COMMAND [...ARGS] Available commands ------------------ `, Version) for _, cmd := range CmdList { var opts string if len(cmd.Args) > 0 { opts = " " + strings.Join(cmd.Args, " ") } if cmd.Private { if showPrivate { fmt.Printf("*%s%s:\n %s\n", cmd.Name, opts, cmd.Desc) } } else { fmt.Printf("%s%s:\n %s\n", cmd.Name, opts, cmd.Desc) } } if showPrivate { fmt.Println("* = private commands") } return }), } direnv-2.21.2/cmd_hook.go000066400000000000000000000017261361411000000151670ustar00rootroot00000000000000package main import ( "fmt" "os" "text/template" ) // HookContext are the variables available during hook template evaluation type HookContext struct { // SelfPath is the unescaped absolute path to direnv SelfPath string } // CmdHook is `direnv hook $0` var CmdHook = &Cmd{ Name: "hook", Desc: "Used to setup the shell hook", Args: []string{"SHELL"}, Action: actionSimple(cmdHookAction), } func cmdHookAction(env Env, args []string) (err error) { var target string if len(args) > 1 { target = args[1] } selfPath, err := os.Executable() if err != nil { return err } ctx := HookContext{selfPath} shell := DetectShell(target) if shell == nil { return fmt.Errorf("unknown target shell '%s'", target) } hookStr, err := shell.Hook() if err != nil { return err } hookTemplate, err := template.New("hook").Parse(hookStr) if err != nil { return err } err = hookTemplate.Execute(os.Stdout, ctx) if err != nil { return err } return } direnv-2.21.2/cmd_prune.go000066400000000000000000000017511361411000000153560ustar00rootroot00000000000000package main import ( "io/ioutil" "os" "path" "strings" ) // CmdPrune is `direnv prune` var CmdPrune = &Cmd{ Name: "prune", Desc: "removes old allowed files", Action: actionWithConfig(cmdPruneAction), } func cmdPruneAction(env Env, args []string, config *Config) (err error) { var dir *os.File var fi os.FileInfo var dirList []string var envrc []byte allowed := config.AllowDir() if dir, err = os.Open(allowed); err != nil { return err } defer dir.Close() if dirList, err = dir.Readdirnames(0); err != nil { return err } for _, hash := range dirList { filename := path.Join(allowed, hash) if fi, err = os.Stat(filename); err != nil { return err } if !fi.IsDir() { if envrc, err = ioutil.ReadFile(filename); err != nil { return err } envrcStr := strings.TrimSpace(string(envrc)) // skip old files, w/o path inside if envrcStr == "" { continue } if !fileExists(envrcStr) { _ = os.Remove(filename) } } } return nil } direnv-2.21.2/cmd_reload.go000066400000000000000000000005311361411000000154660ustar00rootroot00000000000000package main import ( "fmt" ) // CmdReload is `direnv reload` var CmdReload = &Cmd{ Name: "reload", Desc: "triggers an env reload", Action: actionWithConfig(func(env Env, args []string, config *Config) error { foundRC := config.FindRC() if foundRC == nil { return fmt.Errorf(".envrc not found") } return foundRC.Touch() }), } direnv-2.21.2/cmd_show_dump.go000066400000000000000000000011651361411000000162310ustar00rootroot00000000000000package main import ( "encoding/json" "fmt" "os" "github.com/direnv/direnv/gzenv" ) // CmdShowDump is `direnv show_dump` var CmdShowDump = &Cmd{ Name: "show_dump", Desc: "Show the data inside of a dump for debugging purposes", Args: []string{"DUMP"}, Private: true, Action: actionSimple(cmdShowDumpAction), } func cmdShowDumpAction(env Env, args []string) (err error) { if len(args) < 2 { return fmt.Errorf("missing DUMP argument") } var f interface{} err = gzenv.Unmarshal(args[1], &f) if err != nil { return err } e := json.NewEncoder(os.Stdout) e.SetIndent("", " ") return e.Encode(f) } direnv-2.21.2/cmd_status.go000066400000000000000000000023031361411000000155420ustar00rootroot00000000000000package main import ( "fmt" "path/filepath" ) // CmdStatus is `direnv status` var CmdStatus = &Cmd{ Name: "status", Desc: "prints some debug status information", Action: actionWithConfig(func(env Env, args []string, config *Config) error { fmt.Println("direnv exec path", config.SelfPath) fmt.Println("DIRENV_CONFIG", config.ConfDir) fmt.Println("bash_path", config.BashPath) fmt.Println("disable_stdin", config.DisableStdin) fmt.Println("warn_timeout", config.WarnTimeout) fmt.Println("whitelist.prefix", config.WhitelistPrefix) fmt.Println("whitelist.exact", config.WhitelistExact) loadedRC := config.LoadedRC() foundRC := config.FindRC() if loadedRC != nil { formatRC("Loaded", loadedRC) } else { fmt.Println("No .envrc loaded") } if foundRC != nil { formatRC("Found", foundRC) } else { fmt.Println("No .envrc found") } return nil }), } func formatRC(desc string, rc *RC) { workDir := filepath.Dir(rc.path) fmt.Println(desc, "RC path", rc.path) for idx := range *(rc.times.list) { fmt.Println(desc, "watch:", (*rc.times.list)[idx].Formatted(workDir)) } fmt.Println(desc, "RC allowed", rc.Allowed()) fmt.Println(desc, "RC allowPath", rc.allowPath) } direnv-2.21.2/cmd_stdlib.go000066400000000000000000000005611361411000000155040ustar00rootroot00000000000000package main import ( "fmt" "strings" ) // CmdStdlib is `direnv stdlib` var CmdStdlib = &Cmd{ Name: "stdlib", Desc: "Displays the stdlib available in the .envrc execution context", Action: actionWithConfig(func(env Env, args []string, config *Config) error { fmt.Println(strings.Replace(StdLib, "$(command -v direnv)", config.SelfPath, 1)) return nil }), } direnv-2.21.2/cmd_version.go000066400000000000000000000015141361411000000157070ustar00rootroot00000000000000package main import ( "fmt" "strings" "golang.org/x/mod/semver" ) // CmdVersion is `direnv version` var CmdVersion = &Cmd{ Name: "version", Desc: "prints the version (" + Version + ") or checks that direnv is older than VERSION_AT_LEAST.", Args: []string{"[VERSION_AT_LEAST]"}, Aliases: []string{"--version"}, Action: actionSimple(func(env Env, args []string) error { semVersion := "v" + Version if len(args) > 1 { atLeast := args[1] if !strings.HasPrefix(atLeast, "v") { atLeast = "v" + atLeast } if !semver.IsValid(atLeast) { return fmt.Errorf("%s is not a valid semver version", atLeast) } cmp := semver.Compare(semVersion, atLeast) if cmp < 0 { return fmt.Errorf("%s it older than the desired %s", semVersion, atLeast) } } else { fmt.Println(Version) } return nil }), } direnv-2.21.2/cmd_watch.go000066400000000000000000000020051361411000000153240ustar00rootroot00000000000000package main import ( "fmt" "os" ) // CmdWatch is `direnv watch SHELL [PATH...]` var CmdWatch = &Cmd{ Name: "watch", Desc: "Adds a path to the list that direnv watches for changes", Args: []string{"SHELL", "PATH..."}, Private: true, Action: actionSimple(cmdWatchAction), } func cmdWatchAction(env Env, args []string) (err error) { var shellName string if len(args) < 2 { return fmt.Errorf("a path is required to add to the list of watches") } if len(args) >= 2 { shellName = args[1] } else { shellName = "bash" } shell := DetectShell(shellName) if shell == nil { return fmt.Errorf("unknown target shell '%s'", shellName) } watches := NewFileTimes() watchString, ok := env[DIRENV_WATCHES] if ok { err = watches.Unmarshal(watchString) if err != nil { return } } for _, arg := range args[2:] { err = watches.Update(arg) if err != nil { return } } e := make(ShellExport) e.Add(DIRENV_WATCHES, watches.Marshal()) os.Stdout.WriteString(shell.Export(e)) return } direnv-2.21.2/cmd_watch_list.go000066400000000000000000000030111361411000000163550ustar00rootroot00000000000000package main import ( "bufio" "fmt" "io" "os" "strconv" "strings" ) // CmdWatchList is `direnv watch-list` var CmdWatchList = &Cmd{ Name: "watch-list", Desc: "Pipe pairs of `mtime path` to stdin to build a list of files to watch.", Args: []string{"[SHELL]"}, Private: true, Action: actionSimple(watchListCommand), } func watchListCommand(env Env, args []string) (err error) { var shellName string if len(args) >= 2 { shellName = args[1] } else { shellName = "bash" } shell := DetectShell(shellName) if shell == nil { return fmt.Errorf("unknown target shell '%s'", shellName) } watches := NewFileTimes() watchString, ok := env[DIRENV_WATCHES] if ok { err = watches.Unmarshal(watchString) if err != nil { return err } } // Read `mtime path` lines from stdin reader := bufio.NewReader(os.Stdin) i := 1 for { line, err := reader.ReadString('\n') if err == nil { elems := strings.SplitN(line, " ", 2) if len(elems) != 2 { return fmt.Errorf("line %d: expected to contain two elements", i) } mtime, err := strconv.Atoi(elems[0]) if err != nil { return fmt.Errorf("line %d: %s", i, err) } path := elems[1][:len(elems[1])-1] // add to watches err = watches.NewTime(path, int64(mtime), true) if err != nil { return err } } else if err == io.EOF { break } else { return fmt.Errorf("line %d: %s", i, err) } i++ } e := make(ShellExport) e.Add(DIRENV_WATCHES, watches.Marshal()) os.Stdout.WriteString(shell.Export(e)) return } direnv-2.21.2/commands.go000066400000000000000000000044751361411000000152110ustar00rootroot00000000000000package main import ( "fmt" "strings" "time" ) type actionSimple func(env Env, args []string) error func (fn actionSimple) Call(env Env, args []string, config *Config) error { return fn(env, args) } type actionWithConfig func(env Env, args []string, config *Config) error func (fn actionWithConfig) Call(env Env, args []string, config *Config) error { var err error if config == nil { config, err = LoadConfig(env) if err != nil { return err } } return fn(env, args, config) } type action interface { Call(env Env, args []string, config *Config) error } // Cmd represents a direnv sub-command type Cmd struct { Name string Desc string Args []string Aliases []string Private bool Action action } // CmdList contains the list of all direnv sub-commands var CmdList []*Cmd func init() { CmdList = []*Cmd{ CmdAllow, CmdApplyDump, CmdShowDump, CmdDeny, CmdDotEnv, CmdDump, CmdEdit, CmdExec, CmdExpandPath, CmdExport, CmdHelp, CmdHook, CmdPrune, CmdReload, CmdStatus, CmdStdlib, CmdVersion, CmdWatch, CmdWatchList, CmdCurrent, } } func cmdWithWarnTimeout(fn action) action { return actionWithConfig(func(env Env, args []string, config *Config) (err error) { done := make(chan bool, 1) go func() { select { case <-done: return case <-time.After(config.WarnTimeout): logError("(%v) is taking a while to execute. Use CTRL-C to give up.", args) } }() err = fn.Call(env, args, config) done <- true return err }) } // CommandsDispatch is called by the main() function to dispatch to a sub-command func CommandsDispatch(env Env, args []string) error { var command *Cmd var commandName string var commandPrefix string var commandArgs []string if len(args) < 2 { commandName = "help" commandPrefix = args[0] commandArgs = []string{} } else { commandName = args[1] commandPrefix = strings.Join(args[0:2], " ") commandArgs = append([]string{commandPrefix}, args[2:]...) } for _, cmd := range CmdList { if cmd.Name == commandName { command = cmd break } if cmd.Aliases != nil { for _, alias := range cmd.Aliases { if alias == commandName { command = cmd } } } } if command == nil { return fmt.Errorf("command \"%s\" not found", commandPrefix) } return command.Action.Call(env, commandArgs, nil) } direnv-2.21.2/config.go000066400000000000000000000103701361411000000146440ustar00rootroot00000000000000package main import ( "fmt" "os" "os/exec" "path/filepath" "strings" "time" toml "github.com/BurntSushi/toml" "github.com/direnv/direnv/xdg" ) // Config represents the direnv configuration and state. type Config struct { Env Env WorkDir string // Current directory ConfDir string DataDir string SelfPath string BashPath string RCDir string TomlPath string DisableStdin bool WarnTimeout time.Duration WhitelistPrefix []string WhitelistExact map[string]bool } type tomlConfig struct { BashPath string `toml:"bash_path"` DisableStdin bool `toml:"disable_stdin"` WarnTimeout time.Duration `toml:"warn_timeout"` Whitelist whitelist `toml:"whitelist"` } type whitelist struct { Prefix []string Exact []string } // LoadConfig opens up the direnv configuration from the Env. func LoadConfig(env Env) (config *Config, err error) { config = &Config{ Env: env, } config.ConfDir = env[DIRENV_CONFIG] if config.ConfDir == "" { config.ConfDir = xdg.ConfigDir(env, "direnv") } if config.ConfDir == "" { err = fmt.Errorf("couldn't find a configuration directory for direnv") return } var exePath string if exePath, err = os.Executable(); err != nil { err = fmt.Errorf("LoadConfig() os.Executable() failed: %q", err) return } // Fix for mingsys exePath = strings.Replace(exePath, "\\", "/", -1) config.SelfPath = exePath if config.WorkDir, err = os.Getwd(); err != nil { err = fmt.Errorf("LoadConfig() Getwd failed: %q", err) return } config.RCDir = env[DIRENV_DIR] if len(config.RCDir) > 0 && config.RCDir[0:1] == "-" { config.RCDir = config.RCDir[1:] } config.WhitelistPrefix = make([]string, 0) config.WhitelistExact = make(map[string]bool) // Load the TOML config config.TomlPath = filepath.Join(config.ConfDir, "direnv.toml") if _, statErr := os.Stat(config.TomlPath); statErr != nil { config.TomlPath = "" } config.TomlPath = filepath.Join(config.ConfDir, "config.toml") if _, statErr := os.Stat(config.TomlPath); statErr != nil { config.TomlPath = "" } if config.TomlPath != "" { var tomlConf tomlConfig if _, err = toml.DecodeFile(config.TomlPath, &tomlConf); err != nil { err = fmt.Errorf("LoadConfig() failed to parse %s: %q", config.TomlPath, err) return } config.WhitelistPrefix = append(config.WhitelistPrefix, tomlConf.Whitelist.Prefix...) for _, path := range tomlConf.Whitelist.Exact { if !strings.HasSuffix(path, "/.envrc") { path = filepath.Join(path, ".envrc") } config.WhitelistExact[path] = true } config.DisableStdin = tomlConf.DisableStdin config.BashPath = tomlConf.BashPath config.WarnTimeout = tomlConf.WarnTimeout } if config.WarnTimeout == 0 { timeout, err := time.ParseDuration(env.Fetch("DIRENV_WARN_TIMEOUT", "5s")) if err != nil { logError("invalid DIRENV_WARN_TIMEOUT: " + err.Error()) timeout = 5 * time.Second } config.WarnTimeout = timeout } if config.BashPath == "" { if env[DIRENV_BASH] != "" { config.BashPath = env[DIRENV_BASH] } else if bashPath != "" { config.BashPath = bashPath } else if config.BashPath, err = exec.LookPath("bash"); err != nil { err = fmt.Errorf("can't find bash: %q", err) return } } if config.DataDir == "" { config.DataDir = xdg.DataDir(env, "direnv") } if config.DataDir == "" { err = fmt.Errorf("couldn't find a data directory for direnv") return } return } // AllowDir is the folder where all the "allow" files are stored. func (config *Config) AllowDir() string { return filepath.Join(config.DataDir, "allow") } // LoadedRC returns a RC file if any has been loaded func (config *Config) LoadedRC() *RC { if config.RCDir == "" { logDebug("RCDir is blank - loadedRC is nil") return nil } rcPath := filepath.Join(config.RCDir, ".envrc") timesString := config.Env[DIRENV_WATCHES] return RCFromEnv(rcPath, timesString, config) } // FindRC looks for a RC file in the config environment func (config *Config) FindRC() *RC { return FindRC(config.WorkDir, config) } // EnvDiff returns the recorded environment diff that was stored if any. func (config *Config) EnvDiff() (*EnvDiff, error) { if config.Env[DIRENV_DIFF] == "" { return nil, nil } return LoadEnvDiff(config.Env[DIRENV_DIFF]) } direnv-2.21.2/const.go000066400000000000000000000003441361411000000145250ustar00rootroot00000000000000package main //nolint const ( DIRENV_CONFIG = "DIRENV_CONFIG" DIRENV_BASH = "DIRENV_BASH" DIRENV_DEBUG = "DIRENV_DEBUG" DIRENV_DIR = "DIRENV_DIR" DIRENV_WATCHES = "DIRENV_WATCHES" DIRENV_DIFF = "DIRENV_DIFF" ) direnv-2.21.2/default.nix000066400000000000000000000015631361411000000152200ustar00rootroot00000000000000{ pkgs ? import ./nix {} }: with pkgs; buildGoPackage rec { name = "direnv-${version}"; version = lib.fileContents ./version.txt; goPackagePath = "github.com/direnv/direnv"; subPackages = ["."]; src = lib.cleanSource ./.; postConfigure = '' cd $NIX_BUILD_TOP/go/src/$goPackagePath ''; # we have no bash at the moment for windows makeFlags = stdenv.lib.optional (!stdenv.hostPlatform.isWindows) [ "BASH_PATH=${bash}/bin/bash" ]; installPhase = '' mkdir -p $out make install DESTDIR=$bin mkdir -p $bin/share/fish/vendor_conf.d echo "eval ($bin/bin/direnv hook fish)" > $bin/share/fish/vendor_conf.d/direnv.fish ''; meta = with stdenv.lib; { description = "A shell extension that manages your environment"; homepage = https://direnv.net; license = licenses.mit; maintainers = with maintainers; [ zimbatm ]; }; } direnv-2.21.2/docs/000077500000000000000000000000001361411000000137775ustar00rootroot00000000000000direnv-2.21.2/docs/development.md000066400000000000000000000006031361411000000166420ustar00rootroot00000000000000# Development Setup a go environment https://golang.org/doc/install > go >= 1.11 is required Clone the project: $ git clone git@github.com:direnv/direnv.git Build by just typing make: $ cd direnv $ make Test the projects: $ make test To install to /usr/local: $ make install Or to a different location like `~/.local`: $ make install DESTDIR=~/.local direnv-2.21.2/docs/hook.md000066400000000000000000000016071361411000000152650ustar00rootroot00000000000000# Setup For direnv to work properly it needs to be hooked into the shell. Each shell has its own extension mechanism. Once the hook is configured, restart your shell for direnv to be activated. ## BASH Add the following line at the end of the `~/.bashrc` file: ```sh eval "$(direnv hook bash)" ``` Make sure it appears even after rvm, git-prompt and other shell extensions that manipulate the prompt. ## ZSH Add the following line at the end of the `~/.zshrc` file: ```sh eval "$(direnv hook zsh)" ``` ## FISH Add the following line at the end of the `~/.config/fish/config.fish` file: ```fish direnv hook fish | source ``` ## TCSH Add the following line at the end of the `~/.cshrc` file: ```sh eval `direnv hook tcsh` ``` ## Elvish (0.12+) Run: ``` $> direnv hook elvish > ~/.elvish/lib/direnv.elv ``` and add the following line to your `~/.elvish/rc.elv` file: ``` use direnv ``` direnv-2.21.2/docs/installation.md000066400000000000000000000030011361411000000170140ustar00rootroot00000000000000# Installation The installation has two parts. 1. Install the package or binary, which is presented in this document 2. [hook into your shell](hook.md). ## From system packages direnv is packaged for a variety of systems: * [Fedora](https://apps.fedoraproject.org/packages/direnv) * [Arch AUR](https://aur.archlinux.org/packages/direnv/) * [Debian](https://packages.debian.org/search?keywords=direnv&searchon=names&suite=all§ion=all) * [Gentoo go-overlay](https://github.com/Dr-Terrible/go-overlay) * [NetBSD pkgsrc-wip](http://www.pkgsrc.org/wip/) * [NixOS](https://nixos.org/nixos/packages.html#direnv) * [OSX Homebrew](http://brew.sh/) * [openSUSE](https://build.opensuse.org/package/show/openSUSE%3AFactory/direnv) * [MacPorts](https://www.macports.org/) * [Ubuntu](https://packages.ubuntu.com/search?keywords=direnv&searchon=names&suite=all§ion=all) * [GNU Guix](https://www.gnu.org/software/guix/) * [Snap](https://snapcraft.io/direnv) See also: [![Packaging status](https://repology.org/badge/vertical-allrepos/direnv.svg)](https://repology.org/metapackage/direnv) ## From binary builds To install binary builds you can run this bash installer: ``` curl -sfL https://direnv.net/install.sh | bash ``` Binary builds for a variety of architectures are also available for [each release](https://github.com/direnv/direnv/releases). Fetch the binary, `chmod +x direnv` and put it somewhere in your PATH. ## Compile from source See the [Development](development.md) page. # Next step [hook installation](hook.md) direnv-2.21.2/docs/ruby.md000066400000000000000000000067061361411000000153130ustar00rootroot00000000000000# Manage your rubies with direnv and ruby-install direnv is just a shell extension that manages your environment variables depending on the folder you live in. In this article we will explore how it can be used in combination with [ruby-install](https://github.com/postmodern/ruby-install) to manage and select the version of ruby that you want to use in a project. ## The setup First install direnv. This is the quick version on OSX + Bash: ```bash brew install direnv echo 'eval $(direnv hook bash)' >> .bashrc exec $0 ``` Then use [ruby-install](https://github.com/postmodern/ruby-install) to install a couple of ruby versions. We're also creating a couple of aliases for convenience. ``` brew install ruby-install ruby-install ruby 1.9 ruby-install ruby 2.0 cd ~/.rubies ln -s 1.9.3-p448 1.9.3 ln -s 1.9.3-p448 1.9 ln -s 2.0.0-p247 2.0.0 ln -s 2.0.0-p247 2.0 ``` The end goal is that each project will have an `.envrc` file that contains a descriptive syntax like `use ruby 1.9.3` to selects the right ruby version for the project. For that regard we are going to use a couple of commands available in the [direnv stdlib](/man/direnv-stdlib.1.md) and expand it a bit in the ~/.direnvrc file. Add this to the ~/.direnvrc file (you have to create it if it doesn't exist): ```bash # Usage: use ruby # # Loads the specified ruby version into the environent # use_ruby() { local ruby_dir=$HOME/.rubies/$1 load_prefix $ruby_dir layout ruby } ``` That's it. Now in any project you can run `direnv edit .` and add `use ruby 1.9.3` or `use ruby 2.0` in the file like you want and direnv will select the right ruby version when you enter the project's folder. ## A bit of explanation The last part probably needs a bit more explanation. We make use of a couple of commands that are part of the [stdlib](/stdlib.html) which is availabe in the execution context of an envrc. `use` is a command dispatch that's just there to build the `use something something` dsl so that `use ruby ` will translate into `use_ruby `. `load_prefix` will add a couple of things into the environment, notably add `/bin` into the PATH. This is what makes the specified ruby available. And finally `layout ruby` who like `use` translates into the `layout_ruby` function call. It's used to decribe common project layouts. In the stdlib, the ruby layout will configure rubygems (with the `GEM_HOME` environment variable) to install all the gems into the .direnv/ruby/RUBY_VERSION folder under the project root. This is a bit similar to rvm's gemsets except that they live inside your project's folder. It also configures bundler to install wrapper shims into the .direnv/bin folder which allows you to invoke the commands directly instead of prefixing your ruby programs with `bundle exec` all the time. ## Conclusion As you see this approach is not restricted to ruby. You could have various versions of python installed under ~/.pythons and a `use_python` defined in your ~/.direnvrc. Or perl, php, ... This is the good thing about direnv, it's not restricted to a single language. Actually, wouldn't it be great to have all your project's dependencies available when you enter the project folder ? Not only your ruby version but also the exact redis or mysql or ... version that you want to use, without having to start a VM. I think that's definitely possible using something like the [Nix package manager](http://nixos.org/nix/), something that still needs to be explored in a future post. direnv-2.21.2/env.go000066400000000000000000000053131361411000000141700ustar00rootroot00000000000000package main import ( "encoding/json" "os" "strings" "github.com/direnv/direnv/gzenv" ) // Env is a map representation of environment variables. type Env map[string]string // GetEnv turns the classic unix environment variables into a map of // key->values which is more handy to work with. // // NOTE: We don't support having two variables with the same name. // I've never seen it used in the wild but accoding to POSIX // it's allowed. func GetEnv() Env { env := make(Env) for _, kv := range os.Environ() { kv2 := strings.SplitN(kv, "=", 2) key := kv2[0] value := kv2[1] env[key] = value } return env } // CleanContext removes all the direnv-related environment variables. Call // this after reverting the environment, otherwise direnv will just be amnesic // about the previously-loaded environment. func (env Env) CleanContext() { delete(env, DIRENV_DIR) delete(env, DIRENV_WATCHES) delete(env, DIRENV_DIFF) } // LoadEnv unmarshals the env back from a gzenv string func LoadEnv(gzenvStr string) (env Env, err error) { env = make(Env) err = gzenv.Unmarshal(gzenvStr, &env) return } // LoadEnvJSON unmarshals the env back from a JSON string func LoadEnvJSON(jsonBytes []byte) (env Env, err error) { env = make(Env) err = json.Unmarshal(jsonBytes, &env) return env, err } // Copy returns a fresh copy of the env. Because the env is a map under the // hood, we want to get a copy whenever we mutate it and want to keep the // original around. func (env Env) Copy() Env { newEnv := make(Env) for key, value := range env { newEnv[key] = value } return newEnv } // ToGoEnv should really be named ToUnixEnv. It turns the env back into a list // of "key=value" strings like returns by os.Environ(). func (env Env) ToGoEnv() []string { goEnv := make([]string, len(env)) index := 0 for key, value := range env { goEnv[index] = strings.Join([]string{key, value}, "=") index++ } return goEnv } // ToShell outputs the environment into an evaluatable string that is // understood by the target shell func (env Env) ToShell(shell Shell) string { e := make(ShellExport) for key, value := range env { e.Add(key, value) } return shell.Export(e) } // Serialize marshals the env into the gzenv format func (env Env) Serialize() string { return gzenv.Marshal(env) } // Diff returns the diff between the current env and the passed env func (env Env) Diff(other Env) *EnvDiff { return BuildEnvDiff(env, other) } // Fetch tries to get the value associated with the given 'key', or returns // the provided default if none is set. // // Note that empty environment variables are considered to be set. func (env Env) Fetch(key, def string) string { v, ok := env[key] if !ok { v = def } return v } direnv-2.21.2/env_diff.go000066400000000000000000000060641361411000000151640ustar00rootroot00000000000000package main import ( "strings" "github.com/direnv/direnv/gzenv" ) // IgnoredKeys is list of keys we don't want to deal with var IgnoredKeys = map[string]bool{ // direnv env config "DIRENV_CONFIG": true, "DIRENV_BASH": true, // should only be available inside of the .envrc "DIRENV_IN_ENVRC": true, "COMP_WORDBREAKS": true, // Avoids segfaults in bash "PS1": true, // PS1 should not be exported, fixes problem in bash // variables that should change freely "OLDPWD": true, "PWD": true, "SHELL": true, "SHELLOPTS": true, "SHLVL": true, "_": true, } // EnvDiff represents the diff between two environments type EnvDiff struct { Prev map[string]string `json:"p"` Next map[string]string `json:"n"` } // NewEnvDiff is an empty constructor for EnvDiff func NewEnvDiff() *EnvDiff { return &EnvDiff{make(map[string]string), make(map[string]string)} } // BuildEnvDiff analyses the changes between 'e1' and 'e2' and builds an // EnvDiff out of it. func BuildEnvDiff(e1, e2 Env) *EnvDiff { diff := NewEnvDiff() in := func(key string, e Env) bool { _, ok := e[key] return ok } for key := range e1 { if IgnoredEnv(key) { continue } if e2[key] != e1[key] || !in(key, e2) { diff.Prev[key] = e1[key] } } for key := range e2 { if IgnoredEnv(key) { continue } if e2[key] != e1[key] || !in(key, e1) { diff.Next[key] = e2[key] } } return diff } // LoadEnvDiff unmarshalls a gzenv string back into an EnvDiff. func LoadEnvDiff(gzenvStr string) (diff *EnvDiff, err error) { diff = new(EnvDiff) err = gzenv.Unmarshal(gzenvStr, diff) return } // Any returns if the diff contains any changes. func (diff *EnvDiff) Any() bool { return len(diff.Prev) > 0 || len(diff.Next) > 0 } // ToShell applies the env diff as a set of commands that are understood by // the target `shell`. The outputted string is then meant to be evaluated in // the target shell. func (diff *EnvDiff) ToShell(shell Shell) string { e := make(ShellExport) for key := range diff.Prev { _, ok := diff.Next[key] if !ok { e.Remove(key) } } for key, value := range diff.Next { e.Add(key, value) } return shell.Export(e) } // Patch applies the diff to the given env and returns a new env with the // changes applied. func (diff *EnvDiff) Patch(env Env) (newEnv Env) { newEnv = make(Env) for k, v := range env { newEnv[k] = v } for key := range diff.Prev { delete(newEnv, key) } for key, value := range diff.Next { newEnv[key] = value } return newEnv } // Reverse flips the diff so that it applies the other way around. func (diff *EnvDiff) Reverse() *EnvDiff { return &EnvDiff{diff.Next, diff.Prev} } // Serialize marshalls the environment diff to the gzenv format. func (diff *EnvDiff) Serialize() string { return gzenv.Marshal(diff) } //// Utils // IgnoredEnv returns true if the key should be ignored in environment diffs. func IgnoredEnv(key string) bool { if strings.HasPrefix(key, "__fish") { return true } if strings.HasPrefix(key, "BASH_FUNC_") { return true } _, found := IgnoredKeys[key] return found } direnv-2.21.2/env_diff_test.go000066400000000000000000000020151361411000000162130ustar00rootroot00000000000000package main import ( "reflect" "testing" ) func TestEnvDiff(t *testing.T) { diff := &EnvDiff{map[string]string{"FOO": "bar"}, map[string]string{"BAR": "baz"}} out := diff.Serialize() diff2, err := LoadEnvDiff(out) if err != nil { t.Error("parse error", err) } if len(diff2.Prev) != 1 { t.Error("len(diff2.prev) != 1", len(diff2.Prev)) } if len(diff2.Next) != 1 { t.Error("len(diff2.next) != 0", len(diff2.Next)) } } // Issue #114 // Check that empty environment variables correctly appear in the diff func TestEnvDiffEmptyValue(t *testing.T) { before := Env{} after := Env{"FOO": ""} diff := BuildEnvDiff(before, after) if !reflect.DeepEqual(diff.Next, map[string]string(after)) { t.Errorf("diff.Next != after (%#+v != %#+v)", diff.Next, after) } } func TestIgnoredEnv(t *testing.T) { if !IgnoredEnv(DIRENV_BASH) { t.Fail() } if IgnoredEnv(DIRENV_DIFF) { t.Fail() } if !IgnoredEnv("_") { t.Fail() } if !IgnoredEnv("__fish_foo") { t.Fail() } if !IgnoredEnv("__fishx") { t.Fail() } } direnv-2.21.2/env_test.go000066400000000000000000000005031361411000000152230ustar00rootroot00000000000000package main import ( "testing" ) func TestEnv(t *testing.T) { env := Env{"FOO": "bar"} out := env.Serialize() env2, err := LoadEnv(out) if err != nil { t.Error("parse error", err) } if env2["FOO"] != "bar" { t.Error("FOO != bar", env2["FOO"]) } if len(env2) != 1 { t.Error("len != 1", len(env2)) } } direnv-2.21.2/file_times.go000066400000000000000000000106761361411000000155300ustar00rootroot00000000000000package main import ( "fmt" "os" "path/filepath" "time" "github.com/direnv/direnv/gzenv" ) // FileTime represents a single recorded file status type FileTime struct { Path string Modtime int64 Exists bool } // FileTimes represent a record of all the known files and times type FileTimes struct { list *[]FileTime } // NewFileTimes creates a new empty FileTimes func NewFileTimes() (times FileTimes) { list := make([]FileTime, 0) times.list = &list return } // Update gets the latest stats on the path and updates the record. func (times *FileTimes) Update(path string) (err error) { var modtime int64 var exists bool stat, err := getLatestStat(path) if os.IsNotExist(err) { exists = false } else { exists = true if err != nil { return } modtime = stat.ModTime().Unix() } err = times.NewTime(path, modtime, exists) return } // NewTime add the file on path, with modtime and exists flag to the list of known // files. func (times *FileTimes) NewTime(path string, modtime int64, exists bool) (err error) { var time *FileTime path, err = filepath.Abs(path) if err != nil { return } path = filepath.Clean(path) for idx := range *(times.list) { if (*times.list)[idx].Path == path { time = &(*times.list)[idx] break } } if time == nil { newTimes := append(*times.list, FileTime{Path: path}) times.list = &newTimes time = &((*times.list)[len(*times.list)-1]) } time.Modtime = modtime time.Exists = exists return } type checkFailed struct { message string } func (err checkFailed) Error() string { return err.message } // Check validates all the recorded file times func (times *FileTimes) Check() (err error) { if len(*times.list) == 0 { return checkFailed{"Times list is empty"} } for idx := range *times.list { err = (*times.list)[idx].Check() if err != nil { return } } return } // CheckOne compares notes between the given path and the recorded times func (times *FileTimes) CheckOne(path string) (err error) { path, err = filepath.Abs(path) if err != nil { return } for idx := range *times.list { if time := (*times.list)[idx]; time.Path == path { err = time.Check() return } } return checkFailed{fmt.Sprintf("File %q is unknown", path)} } // Check verifies that the file is good and hasn't changed func (times FileTime) Check() (err error) { stat, err := getLatestStat(times.Path) switch { case os.IsNotExist(err): if times.Exists { logDebug("Stat Check: %s: gone", times.Path) return checkFailed{fmt.Sprintf("File %q is missing (Stat)", times.Path)} } case err != nil: logDebug("Stat Check: %s: ERR: %v", times.Path, err) return err case !times.Exists: logDebug("Check: %s: appeared", times.Path) return checkFailed{fmt.Sprintf("File %q newly created", times.Path)} case stat.ModTime().Unix() != times.Modtime: logDebug("Check: %s: stale (stat: %v, lastcheck: %v)", times.Path, stat.ModTime().Unix(), times.Modtime) return checkFailed{fmt.Sprintf("File %q has changed", times.Path)} } logDebug("Check: %s: up to date", times.Path) return nil } // Formatted shows the times in a user-friendly format. func (times *FileTime) Formatted(relDir string) string { timeBytes, err := time.Unix(times.Modtime, 0).MarshalText() if err != nil { timeBytes = []byte("<>") } path, err := filepath.Rel(relDir, times.Path) if err != nil { path = times.Path } return fmt.Sprintf("%q - %s", path, timeBytes) } // Marshal dumps the times into gzenv format func (times *FileTimes) Marshal() string { return gzenv.Marshal(*times.list) } // Unmarshal loads the watches back from gzenv func (times *FileTimes) Unmarshal(from string) error { return gzenv.Unmarshal(from, times.list) } func getLatestStat(path string) (os.FileInfo, error) { var lstatModTime int64 var statModTime int64 // Check the examine-a-symlink case first: lstat, err := os.Lstat(path) if err != nil { logDebug("getLatestStat,Lstat: %s: error: %v", path, err) return nil, err } lstatModTime = lstat.ModTime().Unix() stat, err := os.Stat(path) if err != nil { logDebug("getLatestStat,Stat: %s: error: %v (Lstat time: %v)", path, err, lstatModTime) return nil, err } statModTime = stat.ModTime().Unix() if lstatModTime > statModTime { logDebug("getLatestStat: %s: Lstat: %v, Stat: %v -> preferring Lstat", path, lstatModTime, statModTime) return lstat, nil } logDebug("getLatestStat: %s: Lstat: %v, Stat: %v -> preferring Stat", path, lstatModTime, statModTime) return stat, nil } direnv-2.21.2/file_times_test.go000066400000000000000000000044241361411000000165610ustar00rootroot00000000000000package main import ( "bytes" "encoding/json" "testing" "time" ) func TestUpdate(t *testing.T) { times := NewFileTimes() _ = times.Update("file_times.go") if len(*times.list) != 1 { t.Error("Length of updated list not 1") } if !(*times.list)[0].Exists { t.Error("Existing file marked not existing") } } func TestFTJsons(t *testing.T) { ft := FileTime{"something.txt", time.Now().Unix(), true} marshalled, err := json.Marshal(ft) if err != nil { t.Error("FileTime failed to marshal:", err) } if bytes.NewBuffer(marshalled).String() == "{}" { t.Error(ft, "marshals as empty object") } } func TestRoundTrip(t *testing.T) { watches := NewFileTimes() _ = watches.Update("file_times.go") rtChk := NewFileTimes() _ = rtChk.Unmarshal(watches.Marshal()) compareFTs(t, watches, rtChk, "length", func(ft FileTimes) interface{} { return len(*ft.list) }) compareFTs(t, watches, rtChk, "first path", func(ft FileTimes) interface{} { return (*ft.list)[0].Path }) } func compareFTs(t *testing.T, left, right FileTimes, desc string, compare func(ft FileTimes) (res interface{})) { lc, rc := compare(left), compare(right) if lc != rc { t.Error("FileTimes didn't round trip.", "Original", desc, "was:", lc, "RT", desc, "was:", rc) } } func TestCanonicalAdds(t *testing.T) { fts := NewFileTimes() _ = fts.NewTime("docs/../file_times.go", 0, true) _ = fts.NewTime("file_times.go", 0, true) if len(*fts.list) > 1 { t.Error("Double add of the same file") } } func TestCheckPasses(t *testing.T) { fts := NewFileTimes() _ = fts.Update("file_times.go") err := fts.Check() if err != nil { t.Error("Check that should pass fails with:", err) } } func TestCheckStale(t *testing.T) { fts := NewFileTimes() _ = fts.NewTime("file_times.go", 0, true) err := fts.Check() if err == nil { t.Error("Check that should fail because stale passes") } } func TestCheckAppeared(t *testing.T) { fts := NewFileTimes() _ = fts.NewTime("file_times.go", 0, false) err := fts.Check() if err == nil { t.Error("Check that should fail because appeared passes") } } func TestCheckGone(t *testing.T) { fts := NewFileTimes() _ = fts.NewTime("nosuchfileevarright.go", time.Now().Unix()+1000, true) err := fts.Check() if err == nil { t.Error("Check that should fail because gone passes") } } direnv-2.21.2/go.mod000066400000000000000000000002621361411000000141550ustar00rootroot00000000000000module github.com/direnv/direnv go 1.12 require ( github.com/BurntSushi/toml v0.3.1 github.com/direnv/go-dotenv v0.0.0-20181227095604-4cce6d1a66f7 golang.org/x/mod v0.2.0 ) direnv-2.21.2/go.sum000066400000000000000000000032661361411000000142110ustar00rootroot00000000000000github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/direnv/go-dotenv v0.0.0-20181227095604-4cce6d1a66f7 h1:JsyIu/zmj8ZuCbyTRYczeOaxLsnSDCAmRwHa0jl4YqY= github.com/direnv/go-dotenv v0.0.0-20181227095604-4cce6d1a66f7/go.mod h1:T/OO6ZHPlA9WCvpwKhgyBiWiMcLwbP/rsGgxkxVHk6g= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= direnv-2.21.2/gzenv/000077500000000000000000000000001361411000000142005ustar00rootroot00000000000000direnv-2.21.2/gzenv/gzenv.go000066400000000000000000000025651361411000000156700ustar00rootroot00000000000000// the gzenv format: json+gzip+base64 // a quickly designed format to export the whole environment back into itself package gzenv import ( "bytes" "compress/zlib" "encoding/base64" "encoding/json" "fmt" "io" "strings" ) // Marshal encodes the object into the gzenv format func Marshal(obj interface{}) string { jsonData, err := json.Marshal(obj) if err != nil { panic(fmt.Errorf("marshal(): %v", err)) } zlibData := bytes.NewBuffer([]byte{}) w := zlib.NewWriter(zlibData) // we assume the zlib writer would never fail _, _ = w.Write(jsonData) w.Close() base64Data := base64.URLEncoding.EncodeToString(zlibData.Bytes()) return base64Data } // Unmarshal restores the gzenv format back into a Go object func Unmarshal(gzenv string, obj interface{}) error { gzenv = strings.TrimSpace(gzenv) data, err := base64.URLEncoding.DecodeString(gzenv) if err != nil { return fmt.Errorf("unmarshal() base64 decoding: %v", err) } zlibReader := bytes.NewReader(data) w, err := zlib.NewReader(zlibReader) if err != nil { return fmt.Errorf("unmarshal() zlib opening: %v", err) } envData := bytes.NewBuffer([]byte{}) _, err = io.Copy(envData, w) if err != nil { return fmt.Errorf("unmarshal() zlib decoding: %v", err) } w.Close() err = json.Unmarshal(envData.Bytes(), &obj) if err != nil { return fmt.Errorf("unmarshal() json parsing: %v", err) } return nil } direnv-2.21.2/install.sh000077500000000000000000000036461361411000000150650ustar00rootroot00000000000000#!/usr/bin/env bash # # A good old bash | curl script for direnv. # set -euo pipefail { # Prevent execution if this script was only partially downloaded log() { echo "[installer] $*" >&2 } die() { log "$@" exit 1 } at_exit() { ret=$? if [[ $ret -gt 0 ]]; then log "the script failed with error $ret.\n" \ "\n" \ "To report installation errors, submit an issue to\n" \ " https://github.com/direnv/direnv/issues/new/choose" fi exit "$ret" } trap at_exit EXIT kernel=$(uname -s | tr "[:upper:]" "[:lower:]") case "$(uname -m)" in x86_64) machine=amd64 ;; i686 | i386) machine=386 ;; *) die "Machine $(uname -m) not supported by the installer.\n" \ "Go to https://direnv for alternate installation methods." ;; esac log "kernel=$kernel machine=$machine" : "${use_sudo:=}" : "${bin_path:=}" if [[ -z "$bin_path" ]]; then log "looking for a writeable PATH" for path in $(echo "$PATH" | tr ':' '\n'); do if [[ -w $path ]]; then bin_path=$path break fi done fi if [[ -z "$bin_path" ]]; then die "did not find a writeable path in $PATH" fi echo "bin_path=$bin_path" log "looking for a download URL" download_url=$( curl -fL https://api.github.com/repos/direnv/direnv/releases/latest \ | grep browser_download_url \ | cut -d '"' -f 4 \ | grep "direnv.$kernel.$machine" ) echo "download_url=$download_url" log "downloading" curl -o "$bin_path/direnv" -fL "$download_url" chmod +x "$bin_path/direnv" cat <\fR .PP Returns 0 if the \fIcommand\fP is available. Returns 1 otherwise. It can be a binary in the PATH or a shell function. .PP Example: .PP .RS .nf if has curl; then echo "Yes we do" fi .fi .RE .SS \fB\fCexpand\_path []\fR .PP Outputs the absolute path of \fIrel\_path\fP relative to \fIrelative\_to\fP or the current directory. .PP Example: .PP .RS .nf cd /usr/local/games expand\_path ../foo # output: /usr/local/foo .fi .RE .SS \fB\fCdotenv []\fR .PP Loads a ".env" file into the current environment .SS \fB\fCuser\_rel\_path \fR .PP Transforms an absolute path \fIabs\_path\fP into a user\-relative path if possible. .PP Example: .PP .RS .nf echo $HOME # output: /home/user user\_rel\_path /home/user/my/project # output: \~/my/project user\_rel\_path /usr/local/lib # output: /usr/local/lib .fi .RE .SS \fB\fCfind\_up \fR .PP Outputs the path of \fIfilename\fP when searched from the current directory up to /. Returns 1 if the file has not been found. .PP Example: .PP .RS .nf cd /usr/local/my mkdir \-p project/foo touch bar cd project/foo find\_up bar # output: /usr/local/my/bar .fi .RE .SS \fB\fCsource\_env \fR .PP Loads another \fB\fC\&.envrc\fR either by specifying its path or filename. .PP NOTE: the other \fB\fC\&.envrc\fR is not checked by the security framework. .SS \fB\fCsource\_up []\fR .PP Loads another \fB\fC\&.envrc\fR if found when searching from the parent directory up to /. .PP NOTE: the other \fB\fC\&.envrc\fR is not checked by the security framework. .SS \fB\fCdirenv\_load []\fR .PP Applies the environment generated by running \fIargv\fP as a command. This is useful for adopting the environment of a child process \- cause that process to run "direnv dump" and then wrap the results with direnv\_load. .PP Example: .PP .RS .nf direnv\_load opam\-env exec \-\- direnv dump .fi .RE .SS \fB\fCPATH\_add \fR .PP Prepends the expanded \fIpath\fP to the PATH environment variable. It prevents a common mistake where PATH is replaced by only the new \fIpath\fP\&. .PP Example: .PP .RS .nf pwd # output: /home/user/my/project PATH\_add bin echo $PATH # output: /home/user/my/project/bin:/usr/bin:/bin .fi .RE .SS \fB\fCMANPATH\_add \fR .PP Prepends the expanded \fIpath\fP to the MANPATH environment variable. It takes care of man\-specific heuritic. .SS \fB\fCpath\_add \fR .PP Works like \fB\fCPATH\_add\fR except that it's for an arbitrary \fIvarname\fP\&. .SS \fB\fCload\_prefix \fR .PP Expands some common path variables for the given \fIprefix\_path\fP prefix. This is useful if you installed something in the \fIprefix\_path\fP using \fB\fC\&./configure \-\&\-\&prefix=$prefix\_\&path \&\&\&\& make install\fR and want to use it in the project. .PP Variables set: .PP .RS .nf CPATH LD\_LIBRARY\_PATH LIBRARY\_PATH MANPATH PATH PKG\_CONFIG\_PATH .fi .RE .PP Example: .PP .RS .nf \&./configure \-\&\-\&prefix=$HOME/rubies/ruby\-\&1.9.3 make \&\&\&\& make install # Then in the .envrc load\_\&prefix \~\&/rubies/ruby\-\&1.9.3 .fi .RE .SS \fB\fClayout \fR .PP A semantic dispatch used to describe common project layouts. .SS \fB\fClayout go\fR .PP Sets the GOPATH environment variable to the current directory. .SS \fB\fClayout node\fR .PP Adds "$PWD/node\_modules/.bin" to the PATH environment variable. .SS \fB\fClayout perl\fR .PP Setup environment variables required by perl's local::lib See \[la]http://search.cpan.org/dist/local-lib/lib/local/lib.pm\[ra] for more details. .SS \fB\fClayout python []\fR .PP Creates and loads a virtualenv environment under \fB\fC$PWD/.direnv/python\-$python\_version\fR\&. This forces the installation of any egg into the project's sub\-\&folder. .PP It's possible to specify the python executable if you want to use different versions of python (eg: \fB\fClayout python python3\fR). .PP Note that previously virtualenv was located under \fB\fC$PWD/.direnv/virtualenv\fR and will be re\-used by direnv if it exists. .SS \fB\fClayout python3\fR .PP A shortcut for \fB\fClayout python python3\fR .SS \fB\fClayout ruby\fR .PP Sets the GEM\_HOME environment variable to \fB\fC$PWD/.direnv/ruby/RUBY\_VERSION\fR\&. This forces the installation of any gems into the project's sub\-\&folder. If you're using bundler it will create wrapper programs that can be invoked directly instead of using the \fB\fCbundle exec\fR prefix. .SS \fB\fCuse []\fR .PP A semantic command dispatch intended for loading external dependencies into the environment. .PP Example: .PP .RS .nf use\_ruby() { echo "Ruby $1" } use ruby 1.9.3 # output: Ruby 1.9.3 .fi .RE .SS \fB\fCuse rbenv\fR .PP Loads rbenv which add the ruby wrappers available on the PATH. .SS \fB\fCuse nix [...]\fR .PP Load environment variables from \fB\fCnix\-shell\fR\&. .PP If you have a \fB\fCdefault.nix\fR or \fB\fCshell.nix\fR these will be used by default, but you can also specify packages directly (e.g \fB\fCuse nix \-p ocaml\fR). .PP See \[la]http://nixos.org/nix/manual/#sec-nix-shell\[ra] .SS \fB\fCuse guix [...]\fR .PP Load environment variables from \fB\fCguix environment\fR\&. .PP Any arguments given will be passed to guix environment. For example, \fB\fCuse guix hello\fR would setup an environment with the dependencies of the hello package. To create an environment including hello, the \fB\fC\-\-ad\-hoc\fR flag is used \fB\fCuse guix \-\-ad\-hoc hello\fR\&. Other options include \fB\fC\-\-load\fR which allows loading an environment from a file. .PP See \[la]https://www.gnu.org/software/guix/manual/html_node/Invoking-guix-environment.html\[ra] .SS \fB\fCrvm [...]\fR .PP Should work just like in the shell if you have rvm installed. .SS \fB\fCuse node []\fR: .PP Loads NodeJS version from a \fB\fC\&.node\-\&version\fR or \fB\fC\&.nvmrc\fR file. .PP If you specify a partial NodeJS version (i.e. \fB\fC4.2\fR), a fuzzy match is performed and the highest matching version installed is selected. .PP Example (.envrc): .PP .RS .nf set \-e use node .fi .RE .PP Example (.node\-version): .PP .RS .nf 4.2 .fi .RE .SS \fB\fCuse node \fR .PP Loads specified NodeJS version. .PP Example (.envrc): .PP .RS .nf set \-e use node 4.2.2 .fi .RE .SS \fB\fCwatch\_file [ ...]\fR .PP Adds each file to direnv's watch\-list. If the file changes direnv will reload the environment on the next prompt. .PP Example (.envrc): .PP .RS .nf watch\_file Gemfile .fi .RE .SS \fB\fCdirenv\_version \fR .PP Checks that the direnv version is at least old as \fB\fCversion\_at\_least\fR\&. This can be useful when sharing an \fB\fC\&.envrc\fR and to make sure that the users are up to date. .SH COPYRIGHT .PP MIT licence \- Copyright (C) 2019 @zimbatm and contributors .SH SEE ALSO .PP direnv(1), direnv.toml(1) direnv-2.21.2/man/direnv-stdlib.1.md000066400000000000000000000146121361411000000170550ustar00rootroot00000000000000DIRENV-STDLIB 1 "2019" direnv "User Manuals" ============================================ NAME ---- direnv-stdlib - functions for the `.envrc` SYNOPSIS -------- `direnv stdlib` DESCRIPTION ----------- Outputs a bash script called the *stdlib*. The following commands are included in that script and loaded in the context of an `.envrc`. In addition, it also loads the file in `~/.direnvrc` if it exists. STDLIB ------ ### `has ` Returns 0 if the *command* is available. Returns 1 otherwise. It can be a binary in the PATH or a shell function. Example: if has curl; then echo "Yes we do" fi ### `expand_path []` Outputs the absolute path of *rel_path* relative to *relative_to* or the current directory. Example: cd /usr/local/games expand_path ../foo # output: /usr/local/foo ### `dotenv []` Loads a ".env" file into the current environment ### `user_rel_path ` Transforms an absolute path *abs_path* into a user-relative path if possible. Example: echo $HOME # output: /home/user user_rel_path /home/user/my/project # output: ~/my/project user_rel_path /usr/local/lib # output: /usr/local/lib ### `find_up ` Outputs the path of *filename* when searched from the current directory up to /. Returns 1 if the file has not been found. Example: cd /usr/local/my mkdir -p project/foo touch bar cd project/foo find_up bar # output: /usr/local/my/bar ### `source_env ` Loads another `.envrc` either by specifying its path or filename. NOTE: the other `.envrc` is not checked by the security framework. ### `source_up []` Loads another `.envrc` if found when searching from the parent directory up to /. NOTE: the other `.envrc` is not checked by the security framework. ### `direnv_load []` Applies the environment generated by running *argv* as a command. This is useful for adopting the environment of a child process - cause that process to run "direnv dump" and then wrap the results with direnv_load. Example: direnv_load opam-env exec -- direnv dump ### `PATH_add ` Prepends the expanded *path* to the PATH environment variable. It prevents a common mistake where PATH is replaced by only the new *path*. Example: pwd # output: /home/user/my/project PATH_add bin echo $PATH # output: /home/user/my/project/bin:/usr/bin:/bin ### `MANPATH_add ` Prepends the expanded *path* to the MANPATH environment variable. It takes care of man-specific heuritic. ### `path_add ` Works like `PATH_add` except that it's for an arbitrary *varname*. ### `load_prefix ` Expands some common path variables for the given *prefix_path* prefix. This is useful if you installed something in the *prefix_path* using `./configure --prefix=$prefix_path && make install` and want to use it in the project. Variables set: CPATH LD_LIBRARY_PATH LIBRARY_PATH MANPATH PATH PKG_CONFIG_PATH Example: ./configure --prefix=$HOME/rubies/ruby-1.9.3 make && make install # Then in the .envrc load_prefix ~/rubies/ruby-1.9.3 ### `layout ` A semantic dispatch used to describe common project layouts. ### `layout go` Sets the GOPATH environment variable to the current directory. ### `layout node` Adds "$PWD/node_modules/.bin" to the PATH environment variable. ### `layout perl` Setup environment variables required by perl's local::lib See http://search.cpan.org/dist/local-lib/lib/local/lib.pm for more details. ### `layout python []` Creates and loads a virtualenv environment under `$PWD/.direnv/python-$python_version`. This forces the installation of any egg into the project's sub-folder. It's possible to specify the python executable if you want to use different versions of python (eg: `layout python python3`). Note that previously virtualenv was located under `$PWD/.direnv/virtualenv` and will be re-used by direnv if it exists. ### `layout python3` A shortcut for `layout python python3` ### `layout ruby` Sets the GEM_HOME environment variable to `$PWD/.direnv/ruby/RUBY_VERSION`. This forces the installation of any gems into the project's sub-folder. If you're using bundler it will create wrapper programs that can be invoked directly instead of using the `bundle exec` prefix. ### `use []` A semantic command dispatch intended for loading external dependencies into the environment. Example: use_ruby() { echo "Ruby $1" } use ruby 1.9.3 # output: Ruby 1.9.3 ### `use rbenv` Loads rbenv which add the ruby wrappers available on the PATH. ### `use nix [...]` Load environment variables from `nix-shell`. If you have a `default.nix` or `shell.nix` these will be used by default, but you can also specify packages directly (e.g `use nix -p ocaml`). See http://nixos.org/nix/manual/#sec-nix-shell ### `use guix [...]` Load environment variables from `guix environment`. Any arguments given will be passed to guix environment. For example, `use guix hello` would setup an environment with the dependencies of the hello package. To create an environment including hello, the `--ad-hoc` flag is used `use guix --ad-hoc hello`. Other options include `--load` which allows loading an environment from a file. See https://www.gnu.org/software/guix/manual/html_node/Invoking-guix-environment.html ### `rvm [...]` Should work just like in the shell if you have rvm installed. ### `use node []`: Loads NodeJS version from a `.node-version` or `.nvmrc` file. If you specify a partial NodeJS version (i.e. `4.2`), a fuzzy match is performed and the highest matching version installed is selected. Example (.envrc): set -e use node Example (.node-version): 4.2 ### `use node ` Loads specified NodeJS version. Example (.envrc): set -e use node 4.2.2 ### `watch_file [ ...]` Adds each file to direnv's watch-list. If the file changes direnv will reload the environment on the next prompt. Example (.envrc): watch_file Gemfile ### `direnv_version ` Checks that the direnv version is at least old as `version_at_least`. This can be useful when sharing an `.envrc` and to make sure that the users are up to date. COPYRIGHT --------- MIT licence - Copyright (C) 2019 @zimbatm and contributors SEE ALSO -------- direnv(1), direnv.toml(1) direnv-2.21.2/man/direnv.1000066400000000000000000000101751361411000000151770ustar00rootroot00000000000000.TH DIRENV 1 "2019" direnv "User Manuals" .SH NAME .PP direnv \- unclutter your .profile .SH SYNOPSIS .PP \fB\fCdirenv\fR \fIcommand\fP ... .SH DESCRIPTION .PP \fB\fCdirenv\fR is an environment variable manager for your shell. It knows how to hook into bash, zsh and fish shell to load or unload environment variables depending on your current directory. This allows you to have project\-specific environment variables and not clutter the "\~/.profile" file. .PP Before each prompt it checks for the existence of an \fB\fC\&.envrc\fR file in the current and parent directories. If the file exists, it is loaded into a bash sub\-shell and all exported variables are then captured by direnv and then made available to your current shell, while unset variables are removed. .PP Because direnv is compiled into a single static executable it is fast enough to be unnoticeable on each prompt. It is also language agnostic and can be used to build solutions similar to rbenv, pyenv, phpenv, ... .SH EXAMPLE .PP .RS .nf $ cd \~/my\_project $ echo ${FOO\-nope} nope $ echo export FOO=foo > .envrc .envrc is not allowed $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +FOO $ echo ${FOO\-nope} foo $ cd .. direnv: unloading direnv export: \~PATH $ echo ${FOO\-nope} nope .fi .RE .SH SETUP .PP For direnv to work properly it needs to be hooked into the shell. Each shell has it's own extension mechanism: .SS BASH .PP Add the following line at the end of the \fB\fC\~/.bashrc\fR file: .PP .RS .nf eval "$(direnv hook bash)" .fi .RE .PP Make sure it appears even after rvm, git\-prompt and other shell extensions that manipulate the prompt. .SS ZSH .PP Add the following line at the end of the \fB\fC\~/.zshrc\fR file: .PP .RS .nf eval "$(direnv hook zsh)" .fi .RE .SS FISH .PP Add the following line at the end of the \fB\fC\~/.config/fish/config.fish\fR file: .PP .RS .nf eval (direnv hook fish) .fi .RE .SS TCSH .PP Add the following line at the end of the \fB\fC\~/.cshrc\fR file: .PP .RS .nf eval `direnv hook tcsh` .fi .RE .SS Elvish .PP Run: .PP .RS .nf $> direnv hook elvish > \~/.elvish/lib/direnv.elv .fi .RE .PP and add the following line to your \fB\fC\~/.elvish/rc.elv\fR file: .PP .RS .nf use direnv .fi .RE .SH USAGE .PP In some target folder, create an \fB\fC\&.envrc\fR file and add some export(1) and unset(1) directives in it. .PP On the next prompt you will notice that direnv complains about the \fB\fC\&.envrc\fR being blocked. This is the security mechanism to avoid loading new files automatically. Otherwise any git repo that you pull, or tar archive that you unpack, would be able to wipe your hard drive once you \fB\fCcd\fR into it. .PP So here we are pretty sure that it won't do anything bad. Type \fB\fCdirenv allow .\fR and watch direnv loading your new environment. Note that \fB\fCdirenv edit .\fR is a handy shortcut that open the file in your $EDITOR and automatically allows it if the file's modification time has changed. .PP Now that the environment is loaded you can notice that once you \fB\fCcd\fR out of the directory it automatically gets unloaded. If you \fB\fCcd\fR back into it it's loaded again. That's the base of the mechanism that allows you to build cool things. .PP Exporting variables by hand is a bit repetitive so direnv provides a set of utility functions that are made available in the context of the \fB\fC\&.envrc\fR file. Check the direnv\-stdlib(1) man page for more details. You can also define your own extensions inside \fB\fC\~/.config/direnv/direnvrc\fR or \fB\fC\~/.config/direnv/lib/*.sh\fR files. .PP Hopefully this is enough to get you started. .SH CONTRIBUTE .PP Bug reports, contributions and forks are welcome. .PP All bugs or other forms of discussion happen on \[la]http://github.com/direnv/direnv/issues\[ra] .PP There is also a wiki available where you can share your usage patterns or other tips and tricks \[la]https://github.com/direnv/direnv/wiki\[ra] .PP Or drop by on the #direnv channel on FreeNode \[la]irc://#direnv@FreeNode\[ra] to have a chat. .SH COPYRIGHT .PP MIT licence \- Copyright (C) 2019 @zimbatm and contributors .SH SEE ALSO .PP direnv\-stdlib(1), direnv.toml(1) direnv-2.21.2/man/direnv.1.md000066400000000000000000000075471361411000000156070ustar00rootroot00000000000000DIRENV 1 "2019" direnv "User Manuals" =========================================== NAME ---- direnv - unclutter your .profile SYNOPSIS -------- `direnv` *command* ... DESCRIPTION ----------- `direnv` is an environment variable manager for your shell. It knows how to hook into bash, zsh and fish shell to load or unload environment variables depending on your current directory. This allows you to have project-specific environment variables and not clutter the "~/.profile" file. Before each prompt it checks for the existence of an `.envrc` file in the current and parent directories. If the file exists, it is loaded into a bash sub-shell and all exported variables are then captured by direnv and then made available to your current shell, while unset variables are removed. Because direnv is compiled into a single static executable it is fast enough to be unnoticeable on each prompt. It is also language agnostic and can be used to build solutions similar to rbenv, pyenv, phpenv, ... EXAMPLE ------- ``` $ cd ~/my_project $ echo ${FOO-nope} nope $ echo export FOO=foo > .envrc .envrc is not allowed $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +FOO $ echo ${FOO-nope} foo $ cd .. direnv: unloading direnv export: ~PATH $ echo ${FOO-nope} nope ``` SETUP ----- For direnv to work properly it needs to be hooked into the shell. Each shell has it's own extension mechanism: ### BASH Add the following line at the end of the `~/.bashrc` file: ```sh eval "$(direnv hook bash)" ``` Make sure it appears even after rvm, git-prompt and other shell extensions that manipulate the prompt. ### ZSH Add the following line at the end of the `~/.zshrc` file: ```sh eval "$(direnv hook zsh)" ``` ### FISH Add the following line at the end of the `~/.config/fish/config.fish` file: ```fish eval (direnv hook fish) ``` ### TCSH Add the following line at the end of the `~/.cshrc` file: ```sh eval `direnv hook tcsh` ``` ### Elvish Run: ``` $> direnv hook elvish > ~/.elvish/lib/direnv.elv ``` and add the following line to your `~/.elvish/rc.elv` file: ``` use direnv ``` USAGE ----- In some target folder, create an `.envrc` file and add some export(1) and unset(1) directives in it. On the next prompt you will notice that direnv complains about the `.envrc` being blocked. This is the security mechanism to avoid loading new files automatically. Otherwise any git repo that you pull, or tar archive that you unpack, would be able to wipe your hard drive once you `cd` into it. So here we are pretty sure that it won't do anything bad. Type `direnv allow .` and watch direnv loading your new environment. Note that `direnv edit .` is a handy shortcut that open the file in your $EDITOR and automatically allows it if the file's modification time has changed. Now that the environment is loaded you can notice that once you `cd` out of the directory it automatically gets unloaded. If you `cd` back into it it's loaded again. That's the base of the mechanism that allows you to build cool things. Exporting variables by hand is a bit repetitive so direnv provides a set of utility functions that are made available in the context of the `.envrc` file. Check the direnv-stdlib(1) man page for more details. You can also define your own extensions inside `~/.config/direnv/direnvrc` or `~/.config/direnv/lib/*.sh` files. Hopefully this is enough to get you started. CONTRIBUTE ---------- Bug reports, contributions and forks are welcome. All bugs or other forms of discussion happen on There is also a wiki available where you can share your usage patterns or other tips and tricks Or drop by on the [#direnv channel on FreeNode](irc://#direnv@FreeNode) to have a chat. COPYRIGHT --------- MIT licence - Copyright (C) 2019 @zimbatm and contributors SEE ALSO -------- direnv-stdlib(1), direnv.toml(1) direnv-2.21.2/man/direnv.toml.1000066400000000000000000000077621361411000000161610ustar00rootroot00000000000000.TH DIRENV.TOML 1 "2019" direnv "User Manuals" .SH NAME .PP direnv.toml \- the direnv configuration file .SH DESCRIPTION .PP A configuration file in TOML \[la]https://github.com/toml-lang/toml\[ra] format to specify a variety of configuration options for direnv. Resides at CONFIGURATION\_DIR/direnv.toml. For many users, this will be located at $HOME/.config/direnv/direnv.toml. .SH FORMAT .PP See the TOML GitHub Repository \[la]https://github.com/toml-lang/toml\[ra] for details about the syntax of the configuration file. .SH CONFIG .PP The configuration is specified in sections which each have their own top\-level tables \[la]https://github.com/toml-lang/toml#table\[ra], with key/value pairs specified in each section. .PP Example: .PP .RS .nf [section] key = "value" .fi .RE .PP The following sections are supported: .SH \fB\fCwarn\_timeout\fR .PP Specify how long to wait before warning the user that the command is taking too long to execute. Defaults to "5s". .PP A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "\-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". .SH \fB\fCwhitelist\fR .PP Specifying whitelist directives marks specific directory hierarchies or specific directories as "trusted" \-\- direnv will evaluate any matching .envrc files regardless of whether they have been specifically allowed. \fBThis feature should be used with great care\fP, as anyone with the ability to write files to that directory (including collaborators on VCS repositories) will be able to execute arbitrary code on your computer. .PP There are two types of whitelist directives supported: .SS \fB\fCprefix\fR .PP Accepts an array of strings. If any of the strings in this list are a prefix of an .envrc file's absolute path, that file will be implicitly allowed, regardless of contents or past usage of \fB\fCdirenv allow\fR or \fB\fCdirenv deny\fR\&. .PP Example: .PP .RS .nf [whitelist] prefix = [ "/home/user/code/project\-a" ] .fi .RE .PP In this example, the following .envrc files will be implicitly allowed: .IP \(bu 2 \fB\fC/home/user/code/project\-a/.envrc\fR .IP \(bu 2 \fB\fC/home/user/code/project\-a/subdir/.envrc\fR .IP \(bu 2 and so on .PP In this example, the following .envrc files will not be implicitly allowed (although they can be explicitly allowed by running \fB\fCdirenv allow\fR): .IP \(bu 2 \fB\fC/home/user/project\-b/.envrc\fR .IP \(bu 2 \fB\fC/opt/random/.envrc\fR .SS \fB\fCexact\fR .PP Accepts an array of strings. Each string can be a directory name or the full path to an .envrc file. If a directory name is passed, it will be treated as if it had been passed as itself with \fB\fC/.envrc\fR appended. After resolving the filename, each string will be checked for being an exact match with an .envrc file's absolute path. If they match exactly, that .envrc file will be implicitly allowed, regardless of contents or past usage of \fB\fCdirenv allow\fR or \fB\fCdirenv deny\fR\&. .PP Example: .PP .RS .nf [whitelist] exact = [ "/home/user/project\-b/.envrc", "/home/user/project\-b/subdir\-a" ] .fi .RE .PP In this example, the following .envrc files will be implicitly allowed, and no others: .IP \(bu 2 \fB\fC/home/user/code/project\-b/.envrc\fR .IP \(bu 2 \fB\fC/home/user/code/project\-b/subdir\-a\fR .PP In this example, the following .envrc files will not be implicitly allowed (although they can be explicitly allowed by running \fB\fCdirenv allow\fR): .IP \(bu 2 \fB\fC/home/user/code/project\-b/subproject\-c/.envrc\fR .IP \(bu 2 \fB\fC/home/user/code/.envrc\fR .SH \fB\fCbash\_path\fR .PP This allows one to hard\-code the position of bash. It maybe be useful to set this to avoid having direnv to fail when PATH is being mutated. .SH \fB\fCdisable\_stdin\fR .PP If set to true, stdin is disabled (redirected to /dev/null) during the \fB\fC\&.envrc\fR evaluation. .SH COPYRIGHT .PP MIT licence \- Copyright (C) 2019 @zimbatm and contributors .SH SEE ALSO .PP direnv(1), direnv\-stdlib(1) direnv-2.21.2/man/direnv.toml.1.md000066400000000000000000000072531361411000000165530ustar00rootroot00000000000000DIRENV.TOML 1 "2019" direnv "User Manuals" ========================================== NAME ---- direnv.toml - the direnv configuration file DESCRIPTION ----------- A configuration file in [TOML](https://github.com/toml-lang/toml) format to specify a variety of configuration options for direnv. Resides at CONFIGURATION_DIR/direnv.toml. For many users, this will be located at $HOME/.config/direnv/direnv.toml. FORMAT ------ See the [TOML GitHub Repository](https://github.com/toml-lang/toml) for details about the syntax of the configuration file. CONFIG ------ The configuration is specified in sections which each have their own top-level [tables](https://github.com/toml-lang/toml#table), with key/value pairs specified in each section. Example: ```toml [section] key = "value" ``` The following sections are supported: ## `warn_timeout` Specify how long to wait before warning the user that the command is taking too long to execute. Defaults to "5s". A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". ## `whitelist` Specifying whitelist directives marks specific directory hierarchies or specific directories as "trusted" -- direnv will evaluate any matching .envrc files regardless of whether they have been specifically allowed. **This feature should be used with great care**, as anyone with the ability to write files to that directory (including collaborators on VCS repositories) will be able to execute arbitrary code on your computer. There are two types of whitelist directives supported: ### `prefix` Accepts an array of strings. If any of the strings in this list are a prefix of an .envrc file's absolute path, that file will be implicitly allowed, regardless of contents or past usage of `direnv allow` or `direnv deny`. Example: ```toml [whitelist] prefix = [ "/home/user/code/project-a" ] ``` In this example, the following .envrc files will be implicitly allowed: * `/home/user/code/project-a/.envrc` * `/home/user/code/project-a/subdir/.envrc` * and so on In this example, the following .envrc files will not be implicitly allowed (although they can be explicitly allowed by running `direnv allow`): * `/home/user/project-b/.envrc` * `/opt/random/.envrc` ### `exact` Accepts an array of strings. Each string can be a directory name or the full path to an .envrc file. If a directory name is passed, it will be treated as if it had been passed as itself with `/.envrc` appended. After resolving the filename, each string will be checked for being an exact match with an .envrc file's absolute path. If they match exactly, that .envrc file will be implicitly allowed, regardless of contents or past usage of `direnv allow` or `direnv deny`. Example: ```toml [whitelist] exact = [ "/home/user/project-b/.envrc", "/home/user/project-b/subdir-a" ] ``` In this example, the following .envrc files will be implicitly allowed, and no others: * `/home/user/code/project-b/.envrc` * `/home/user/code/project-b/subdir-a` In this example, the following .envrc files will not be implicitly allowed (although they can be explicitly allowed by running `direnv allow`): * `/home/user/code/project-b/subproject-c/.envrc` * `/home/user/code/.envrc` ## `bash_path` This allows one to hard-code the position of bash. It maybe be useful to set this to avoid having direnv to fail when PATH is being mutated. ## `disable_stdin` If set to true, stdin is disabled (redirected to /dev/null) during the `.envrc` evaluation. COPYRIGHT --------- MIT licence - Copyright (C) 2019 @zimbatm and contributors SEE ALSO -------- direnv(1), direnv-stdlib(1) direnv-2.21.2/nix/000077500000000000000000000000001361411000000136455ustar00rootroot00000000000000direnv-2.21.2/nix/default.nix000066400000000000000000000001371361411000000160120ustar00rootroot00000000000000{ sources ? import ./sources.nix }: import sources.nixpkgs { config = {}; overlays = []; } direnv-2.21.2/nix/sources.json000066400000000000000000000010331361411000000162200ustar00rootroot00000000000000{ "nixpkgs": { "url": "https://github.com/nixos/nixpkgs-channels/archive/80b42e630b23052d9525840a9742100a2ceaaa8f.tar.gz", "owner": "nixos", "branch": "nixos-19.09", "url_template": "https://github.com///archive/.tar.gz", "repo": "nixpkgs-channels", "sha256": "0243qiivxl3z51biy4f5y5cy81x5bki5dazl9wqwgnmd373gpmxy", "description": "Nixpkgs/NixOS branches that track the Nixpkgs/NixOS channels", "rev": "80b42e630b23052d9525840a9742100a2ceaaa8f" } } direnv-2.21.2/nix/sources.nix000066400000000000000000000017011361411000000160470ustar00rootroot00000000000000# A record, from name to path, of the third-party packages with { sources = builtins.fromJSON (builtins.readFile ./sources.json); # fetchTarball version that is compatible between all the sources of Nix fetchTarball = { url, sha256 }: if builtins.lessThan builtins.nixVersion "1.12" then builtins.fetchTarball { inherit url; } else builtins.fetchTarball { inherit url sha256; }; mapAttrs = builtins.mapAttrs or (f: set: with builtins; listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))); }; # NOTE: spec must _not_ have an "outPath" attribute mapAttrs (_: spec: if builtins.hasAttr "outPath" spec then abort "The values in sources.json should not have an 'outPath' attribute" else if builtins.hasAttr "url" spec && builtins.hasAttr "sha256" spec then spec // { outPath = fetchTarball { inherit (spec) url sha256; } ; } else spec ) sources direnv-2.21.2/rc.go000066400000000000000000000126041361411000000140050ustar00rootroot00000000000000package main import ( "context" "crypto/sha256" "fmt" "io" "io/ioutil" "os" "os/exec" "path/filepath" "strings" "time" ) // RC represents the .envrc file type RC struct { path string allowPath string times FileTimes config *Config } // FindRC looks the RC file from the wd, up to the root func FindRC(wd string, config *Config) *RC { rcPath := findUp(wd, ".envrc") if rcPath == "" { return nil } return RCFromPath(rcPath, config) } // RCFromPath inits the RC from a given path func RCFromPath(path string, config *Config) *RC { hash, err := fileHash(path) if err != nil { return nil } allowPath := filepath.Join(config.AllowDir(), hash) times := NewFileTimes() err = times.Update(path) if err != nil { return nil } err = times.Update(allowPath) if err != nil { return nil } return &RC{path, allowPath, times, config} } // RCFromEnv inits the RC from the environment func RCFromEnv(path, marshalledTimes string, config *Config) *RC { times := NewFileTimes() err := times.Unmarshal(marshalledTimes) if err != nil { return nil } return &RC{path, "", times, config} } // Allow grants the RC as allowed to load func (rc *RC) Allow() (err error) { if rc.allowPath == "" { return fmt.Errorf("cannot allow empty path") } if err = os.MkdirAll(filepath.Dir(rc.allowPath), 0755); err != nil { return } if err = allow(rc.path, rc.allowPath); err != nil { return } err = rc.times.Update(rc.allowPath) return } // Deny revokes the permission of the RC file to load func (rc *RC) Deny() error { return os.Remove(rc.allowPath) } // Allowed checks if the RC file has been granted loading func (rc *RC) Allowed() bool { // happy path is if this envrc has been explicitly allowed, O(1)ish common case _, err := os.Stat(rc.allowPath) if err == nil { return true } // when whitelisting we want to be (path) absolutely sure we've not been duped with a symlink path, err := filepath.Abs(rc.path) // seems unlikely that we'd hit this, but have to handle it if err != nil { return false } // exact whitelists are O(1)ish to check, so look there first if rc.config.WhitelistExact[path] { return true } // finally we check if any of our whitelist prefixes match for _, prefix := range rc.config.WhitelistPrefix { if strings.HasPrefix(path, prefix) { return true } } return false } // Path returns the path to the RC file func (rc *RC) Path() string { return rc.path } // Touch updates the mtime of the RC file. This is mainly used to trigger a // reload in direnv. func (rc *RC) Touch() error { return touch(rc.path) } const notAllowed = "%s is blocked. Run `direnv allow` to approve its content" // Load evaluates the RC file and returns the new Env or error. // // This functions is key to the implementation of direnv. func (rc *RC) Load(ctx context.Context, config *Config, env Env) (newEnv Env, err error) { wd := config.WorkDir direnv := config.SelfPath newEnv = env.Copy() newEnv[DIRENV_WATCHES] = rc.times.Marshal() defer func() { rc.RecordState(env, newEnv) }() if !rc.Allowed() { err = fmt.Errorf(notAllowed, rc.Path()) return } arg := fmt.Sprintf( `eval "$("%s" stdlib)" && __main__ source_env "%s"`, direnv, rc.Path(), ) cmd := exec.CommandContext(ctx, config.BashPath, "--noprofile", "--norc", "-c", arg) cmd.Dir = wd cmd.Env = newEnv.ToGoEnv() cmd.Stderr = os.Stderr if config.DisableStdin { cmd.Stdin, err = os.Open(os.DevNull) if err != nil { return } } else { cmd.Stdin = os.Stdin } out, err := cmd.Output() if err != nil { return } if len(out) > 0 { var newEnv2 Env newEnv2, err = LoadEnvJSON(out) if err != nil { return } if newEnv2["PS1"] != "" { logError("PS1 cannot be exported. For more information see https://github.com/direnv/direnv/wiki/PS1") } newEnv = newEnv2 } return } // RecordState just applies the new environment func (rc *RC) RecordState(env Env, newEnv Env) { newEnv[DIRENV_DIR] = "-" + filepath.Dir(rc.path) newEnv[DIRENV_DIFF] = env.Diff(newEnv).Serialize() } /// Utils func rootDir(path string) string { path, err := filepath.Abs(path) if err != nil { panic(err) } i := strings.Index(path[1:], "/") if i < 0 { return path } return path[:i+1] } func eachDir(path string) (paths []string) { path, err := filepath.Abs(path) if err != nil { return } paths = []string{path} if path == "/" { return } for i := len(path) - 1; i >= 0; i-- { if path[i] == os.PathSeparator { path = path[:i] if path == "" { path = "/" } paths = append(paths, path) } } return } func fileExists(path string) bool { _, err := os.Stat(path) return err == nil } func fileHash(path string) (hash string, err error) { if path, err = filepath.Abs(path); err != nil { return } fd, err := os.Open(path) if err != nil { return } hasher := sha256.New() _, err = hasher.Write([]byte(path + "\n")) if err != nil { return } if _, err = io.Copy(hasher, fd); err != nil { return } return fmt.Sprintf("%x", hasher.Sum(nil)), nil } // Creates a file func touch(path string) (err error) { t := time.Now() return os.Chtimes(path, t, t) } func allow(path string, allowPath string) (err error) { return ioutil.WriteFile(allowPath, []byte(path+"\n"), 0644) } func findUp(searchDir string, fileName string) (path string) { for _, dir := range eachDir(searchDir) { path = filepath.Join(dir, fileName) if fileExists(path) { return } } return "" } direnv-2.21.2/rc_test.go000066400000000000000000000005651361411000000150470ustar00rootroot00000000000000package main import ( "testing" ) func TestSomething(t *testing.T) { paths := eachDir("/foo/b//bar/") if len(paths) != 4 { t.Fail() } paths = eachDir("/") if len(paths) != 1 && paths[0] != "/" { t.Fail() } } func TestRootDir(t *testing.T) { r := rootDir("/foo") if r != "/foo" { t.Error(r) } r = rootDir("/foo/bar") if r != "/foo" { t.Error(r) } } direnv-2.21.2/script/000077500000000000000000000000001361411000000143535ustar00rootroot00000000000000direnv-2.21.2/script/dist-push000077500000000000000000000041741361411000000162270ustar00rootroot00000000000000#!/usr/bin/env bash # # Upload binary artifacts when a new release is made. # # Usage: ./script/dist-push [] # # Depends on: bash, coreutils, jq, curl set -euo pipefail # Ensure that the GITHUB_TOKEN secret is included if [[ -z "${GITHUB_TOKEN:-}" ]]; then echo "Set the GITHUB_TOKEN env variable." exit 1 fi # Prepare the headers for our curl-command. AUTH_HEADER="Authorization: token ${GITHUB_TOKEN}" # Set the github repository in CI : "${GITHUB_REPOSITORY:=direnv/direnv}" # Create the correct Upload URL. if [[ -n "${1:-}" ]]; then RELEASE_ID=$(curl -sfL \ -H "${AUTH_HEADER}" \ "https://api.github.com/repos/$GITHUB_REPOSITORY/releases/tags/$1" \ | jq .id) else # if not tag is given, assume we are in CI RELEASE_ID=$(jq --raw-output '.release.id' "$GITHUB_EVENT_PATH") fi # start from the project root cd "$(dirname "$0")/.." # make sure we have all the dist files make dist # For each matching file.. for file in dist/*; do echo "Processing '${file}'" filename=$(basename "${file}") upload_url="https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=${filename}" echo "Upload URL is '${upload_url}'" # Generate a temporary file. tmp=$(mktemp) # Upload the artifact - capturing HTTP response-code in our output file. if ! response=$( curl \ -sSL \ -XPOST \ -H "${AUTH_HEADER}" \ --upload-file "${file}" \ --header "Content-Type:application/octet-stream" \ --write-out "%{http_code}" \ --output "$tmp" \ "${upload_url}" ); then echo "**********************************" echo " curl command did not return zero." echo " Aborting" echo "**********************************" cat "$tmp" rm "$tmp" exit 1 fi # If upload is not successful, we must abort if [[ $response -ge 400 ]]; then echo "***************************" echo " upload was not successful." echo " Aborting" echo " HTTP status is $response" echo "**********************************" cat "$tmp" rm "$tmp" exit 1 fi # Show pretty output, since we already have jq jq . <"$tmp" rm "$tmp" done direnv-2.21.2/script/str2go000077500000000000000000000001311361411000000155140ustar00rootroot00000000000000#!/bin/sh # # Usage: str2go # exec go run "$0.go" -- "$@" direnv-2.21.2/script/str2go.go000066400000000000000000000017631361411000000161310ustar00rootroot00000000000000package main import ( "bufio" "flag" "fmt" "io" "os" "unicode" ) const ( backSlash = '\\' newLine = '\n' doubleQuote = '"' ) func printRune(w *bufio.Writer, r rune) { switch r { case backSlash: _, _ = w.WriteRune(backSlash) _, _ = w.WriteRune(backSlash) case newLine: _, _ = w.WriteString("\\n\" +\n\t\"") case doubleQuote: _, _ = w.WriteRune(backSlash) _, _ = w.WriteRune(doubleQuote) default: if !isASCII(r) { panic("only ASCII is supported") } _, _ = w.WriteRune(r) } } func isASCII(r rune) bool { return r < unicode.MaxASCII } func main() { flag.Parse() packageName := flag.Arg(0) constantName := flag.Arg(1) in := bufio.NewReader(os.Stdin) out := bufio.NewWriter(os.Stdout) defer out.Flush() fmt.Fprintf(out, "package %s\n\n// %s ...\nconst %s = \"", packageName, constantName, constantName) for { r, _, err := in.ReadRune() if err != nil { if err == io.EOF { break } panic(err) } printRune(out, r) } fmt.Fprint(out, "\"\n") } direnv-2.21.2/shell.go000066400000000000000000000025711361411000000145120ustar00rootroot00000000000000package main import ( "path/filepath" ) // Shell is the interface that represents the interaction with the host shell. type Shell interface { // Hook is the string that gets evaluated into the host shell config and // setups direnv as a prompt hook. Hook() (string, error) // Export outputs the ShellExport as an evaluatable string on the host shell Export(e ShellExport) string // Dump outputs and evaluatable string that sets the env in the host shell Dump(env Env) string } // ShellExport represents environment variables to add and remove on the host // shell. type ShellExport map[string]*string // Add represents the additon of a new environment variable func (e ShellExport) Add(key, value string) { e[key] = &value } // Remove represents the removal of a given `key` environment variable. func (e ShellExport) Remove(key string) { e[key] = nil } // DetectShell returns a Shell instance from the given target. // // target is usually $0 and can also be prefixed by `-` func DetectShell(target string) Shell { target = filepath.Base(target) // $0 starts with "-" if target[0:1] == "-" { target = target[1:] } switch target { case "bash": return Bash case "zsh": return Zsh case "fish": return Fish case "gzenv": return GzEnv case "vim": return Vim case "tcsh": return Tcsh case "json": return JSON case "elvish": return Elvish } return nil } direnv-2.21.2/shell.nix000066400000000000000000000005021361411000000146730ustar00rootroot00000000000000{ pkgs ? import ./nix {} }: with pkgs; mkShell { buildInputs = [ # Build gitAndTools.git-extras # for git-changelog gnumake go go-md2man gox # Shells bashInteractive elvish fish tcsh zsh # Test dependencies golangci-lint ruby shellcheck shfmt ]; } direnv-2.21.2/shell_bash.go000066400000000000000000000063331361411000000155070ustar00rootroot00000000000000package main import "fmt" type bash struct{} // Bash shell instance var Bash Shell = bash{} const bashHook = ` _direnv_hook() { local previous_exit_status=$?; trap -- '' SIGINT; eval "$("{{.SelfPath}}" export bash)"; trap - SIGINT; return $previous_exit_status; }; if ! [[ "${PROMPT_COMMAND:-}" =~ _direnv_hook ]]; then PROMPT_COMMAND="_direnv_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}" fi ` func (sh bash) Hook() (string, error) { return bashHook, nil } func (sh bash) Export(e ShellExport) (out string) { for key, value := range e { if value == nil { out += sh.unset(key) } else { out += sh.export(key, *value) } } return out } func (sh bash) Dump(env Env) (out string) { for key, value := range env { out += sh.export(key, value) } return out } func (sh bash) export(key, value string) string { return "export " + sh.escape(key) + "=" + sh.escape(value) + ";" } func (sh bash) unset(key string) string { return "unset " + sh.escape(key) + ";" } func (sh bash) escape(str string) string { return BashEscape(str) } /* * Escaping */ //nolint const ( ACK = 6 TAB = 9 LF = 10 CR = 13 US = 31 SPACE = 32 AMPERSTAND = 38 SINGLE_QUOTE = 39 PLUS = 43 NINE = 57 QUESTION = 63 LOWERCASE_Z = 90 OPEN_BRACKET = 91 BACKSLASH = 92 UNDERSCORE = 95 CLOSE_BRACKET = 93 BACKTICK = 96 TILDA = 126 DEL = 127 ) // https://github.com/solidsnack/shell-escape/blob/master/Text/ShellEscape/Bash.hs /* A Bash escaped string. The strings are wrapped in @$\'...\'@ if any bytes within them must be escaped; otherwise, they are left as is. Newlines and other control characters are represented as ANSI escape sequences. High bytes are represented as hex codes. Thus Bash escaped strings will always fit on one line and never contain non-ASCII bytes. */ func BashEscape(str string) string { if str == "" { return "''" } in := []byte(str) out := "" i := 0 l := len(in) escape := false hex := func(char byte) { escape = true out += fmt.Sprintf("\\x%02x", char) } backslash := func(char byte) { escape = true out += string([]byte{BACKSLASH, char}) } escaped := func(str string) { escape = true out += str } quoted := func(char byte) { escape = true out += string([]byte{char}) } literal := func(char byte) { out += string([]byte{char}) } for i < l { char := in[i] switch { case char == ACK: hex(char) case char == TAB: escaped(`\t`) case char == LF: escaped(`\n`) case char == CR: escaped(`\r`) case char <= US: hex(char) case char <= AMPERSTAND: quoted(char) case char == SINGLE_QUOTE: backslash(char) case char <= PLUS: quoted(char) case char <= NINE: literal(char) case char <= QUESTION: quoted(char) case char <= LOWERCASE_Z: literal(char) case char == OPEN_BRACKET: quoted(char) case char == BACKSLASH: backslash(char) case char == UNDERSCORE: literal(char) case char <= CLOSE_BRACKET: quoted(char) case char <= BACKTICK: quoted(char) case char <= TILDA: quoted(char) case char == DEL: hex(char) default: hex(char) } i++ } if escape { out = "$'" + out + "'" } return out } direnv-2.21.2/shell_elvish.go000066400000000000000000000015661361411000000160670ustar00rootroot00000000000000package main import ( "bytes" "encoding/json" ) type elvish struct{} // Elvish add support for the elvish shell var Elvish Shell = elvish{} func (elvish) Hook() (string, error) { return `## hook for direnv @edit:before-readline = $@edit:before-readline { try { m = [("{{.SelfPath}}" export elvish | from-json)] if (> (count $m) 0) { m = (explode $m) keys $m | each [k]{ if $m[$k] { set-env $k $m[$k] } else { unset-env $k } } } } except e { echo $e } } `, nil } func (sh elvish) Export(e ShellExport) string { buf := new(bytes.Buffer) err := json.NewEncoder(buf).Encode(e) if err != nil { panic(err) } return buf.String() } func (sh elvish) Dump(env Env) (out string) { buf := new(bytes.Buffer) err := json.NewEncoder(buf).Encode(env) if err != nil { panic(err) } return buf.String() } var ( _ Shell = (*elvish)(nil) ) direnv-2.21.2/shell_fish.go000066400000000000000000000034051361411000000155200ustar00rootroot00000000000000package main import ( "fmt" "strings" ) type fish struct{} // Fish adds support for the fish shell as a host var Fish Shell = fish{} const fishHook = ` function __direnv_export_eval --on-event fish_postexec; "{{.SelfPath}}" export fish | source; end ` func (sh fish) Hook() (string, error) { return fishHook, nil } func (sh fish) Export(e ShellExport) (out string) { for key, value := range e { if value == nil { out += sh.unset(key) } else { out += sh.export(key, *value) } } return out } func (sh fish) Dump(env Env) (out string) { for key, value := range env { out += sh.export(key, value) } return out } func (sh fish) export(key, value string) string { if key == "PATH" { command := "set -x -g PATH" for _, path := range strings.Split(value, ":") { command += " " + sh.escape(path) } return command + ";" } return "set -x -g " + sh.escape(key) + " " + sh.escape(value) + ";" } func (sh fish) unset(key string) string { return "set -e -g " + sh.escape(key) + ";" } func (sh fish) escape(str string) string { in := []byte(str) out := "'" i := 0 l := len(in) hex := func(char byte) { out += fmt.Sprintf("'\\x%02x'", char) } backslash := func(char byte) { out += string([]byte{BACKSLASH, char}) } escaped := func(str string) { out += "'" + str + "'" } literal := func(char byte) { out += string([]byte{char}) } for i < l { char := in[i] switch { case char == TAB: escaped(`\t`) case char == LF: escaped(`\n`) case char == CR: escaped(`\r`) case char <= US: hex(char) case char == SINGLE_QUOTE: backslash(char) case char == BACKSLASH: backslash(char) case char <= TILDA: literal(char) case char == DEL: hex(char) default: hex(char) } i++ } out += "'" return out } direnv-2.21.2/shell_gzenv.go000066400000000000000000000007001361411000000157130ustar00rootroot00000000000000package main import ( "errors" "github.com/direnv/direnv/gzenv" ) type gzenvShell int // GzEnv is not a real shell. used for internal purposes. var GzEnv Shell = gzenvShell(0) func (s gzenvShell) Hook() (string, error) { return "", errors.New("the gzenv shell doesn't support hooking") } func (s gzenvShell) Export(e ShellExport) string { return gzenv.Marshal(e) } func (s gzenvShell) Dump(env Env) string { return gzenv.Marshal(env) } direnv-2.21.2/shell_json.go000066400000000000000000000013311361411000000155340ustar00rootroot00000000000000package main import ( "encoding/json" "errors" ) // jsonShell is not a real shell type jsonShell struct{} // JSON is not really a shell but it fits. Useful to add support to editor and // other external tools that understand JSON as a format. var JSON Shell = jsonShell{} func (sh jsonShell) Hook() (string, error) { return "", errors.New("this feature is not supported") } func (sh jsonShell) Export(e ShellExport) string { out, err := json.MarshalIndent(e, "", " ") if err != nil { // Should never happen panic(err) } return string(out) } func (sh jsonShell) Dump(env Env) string { out, err := json.MarshalIndent(env, "", " ") if err != nil { // Should never happen panic(err) } return string(out) } direnv-2.21.2/shell_tcsh.go000066400000000000000000000042641361411000000155340ustar00rootroot00000000000000package main import ( "fmt" "strings" ) type tcsh struct{} // Tcsh adds support for the tickle shell var Tcsh Shell = tcsh{} func (sh tcsh) Hook() (string, error) { return "alias precmd 'eval `{{.SelfPath}} export tcsh`'", nil } func (sh tcsh) Export(e ShellExport) (out string) { for key, value := range e { if value == nil { out += sh.unset(key) } else { out += sh.export(key, *value) } } return out } func (sh tcsh) Dump(env Env) (out string) { for key, value := range env { out += sh.export(key, value) } return out } func (sh tcsh) export(key, value string) string { if key == "PATH" { command := "set path = (" for _, path := range strings.Split(value, ":") { command += " " + sh.escape(path) } return command + " );" } return "setenv " + sh.escape(key) + " " + sh.escape(value) + " ;" } func (sh tcsh) unset(key string) string { return "unsetenv " + sh.escape(key) + " ;" } func (sh tcsh) escape(str string) string { if str == "" { return "''" } in := []byte(str) out := "" i := 0 l := len(in) hex := func(char byte) { out += fmt.Sprintf("\\x%02x", char) } backslash := func(char byte) { out += string([]byte{BACKSLASH, char}) } escaped := func(str string) { out += str } quoted := func(char byte) { out += string([]byte{char}) } literal := func(char byte) { out += string([]byte{char}) } for i < l { char := in[i] switch { case char == ACK: hex(char) case char == TAB: escaped(`\t`) case char == LF: escaped(`\n`) case char == CR: escaped(`\r`) case char == SPACE: backslash(char) case char <= US: hex(char) case char <= AMPERSTAND: quoted(char) case char == SINGLE_QUOTE: backslash(char) case char <= PLUS: quoted(char) case char <= NINE: literal(char) case char <= QUESTION: quoted(char) case char <= LOWERCASE_Z: literal(char) case char == OPEN_BRACKET: quoted(char) case char == BACKSLASH: backslash(char) case char == UNDERSCORE: literal(char) case char <= CLOSE_BRACKET: quoted(char) case char <= BACKTICK: quoted(char) case char <= TILDA: quoted(char) case char == DEL: hex(char) default: hex(char) } i++ } return out } direnv-2.21.2/shell_test.go000066400000000000000000000015511361411000000155460ustar00rootroot00000000000000package main import ( "testing" ) func TestBashEscape(t *testing.T) { assertEqual(t, `''`, BashEscape("")) assertEqual(t, `$'escape\'quote'`, BashEscape("escape'quote")) assertEqual(t, `$'foo\r\n\tbar'`, BashEscape("foo\r\n\tbar")) assertEqual(t, `$'foo bar'`, BashEscape("foo bar")) assertEqual(t, `$'\xc3\xa9'`, BashEscape("é")) } func TestShellDetection(t *testing.T) { assertNotNil(t, DetectShell("-bash")) assertNotNil(t, DetectShell("-/bin/bash")) assertNotNil(t, DetectShell("-/usr/local/bin/bash")) assertNotNil(t, DetectShell("-zsh")) assertNotNil(t, DetectShell("-/bin/zsh")) assertNotNil(t, DetectShell("-/usr/local/bin/zsh")) } func assertNotNil(t *testing.T, a Shell) { if a == nil { t.Error("Expected not to be nil") } } func assertEqual(t *testing.T, a, b string) { if a != b { t.Errorf("Expected \"%v\" to equal \"%v\"", b, a) } } direnv-2.21.2/shell_vim.go000066400000000000000000000020671361411000000153650ustar00rootroot00000000000000package main import ( "errors" "strings" ) type vim struct{} // Vim adds support for vim. Not really a shell but it's handly. var Vim Shell = vim{} func (sh vim) Hook() (string, error) { return "", errors.New("this feature is not supported. Install the direnv.vim plugin instead") } func (sh vim) Export(e ShellExport) (out string) { for key, value := range e { if value == nil { out += sh.unset(key) } else { out += sh.export(key, *value) } } return out } func (sh vim) Dump(env Env) (out string) { for key, value := range env { out += sh.export(key, value) } return out } func (sh vim) export(key, value string) string { return "let $" + sh.escapeKey(key) + " = " + sh.escapeValue(value) + "\n" } func (sh vim) unset(key string) string { return "let $" + sh.escapeKey(key) + " = ''\n" } // TODO: support keys with special chars or fail func (sh vim) escapeKey(str string) string { return str } // TODO: Make sure this escaping is valid func (sh vim) escapeValue(str string) string { return "'" + strings.Replace(str, "'", "''", -1) + "'" } direnv-2.21.2/shell_zsh.go000066400000000000000000000022551361411000000153750ustar00rootroot00000000000000package main // ZSH is a singleton instance of ZSH_T type zsh struct{} // Zsh adds support for the venerable Z shell. var Zsh Shell = zsh{} const zshHook = ` _direnv_hook() { trap -- '' SIGINT; eval "$("{{.SelfPath}}" export zsh)"; trap - SIGINT; } typeset -ag precmd_functions; if [[ -z ${precmd_functions[(r)_direnv_hook]} ]]; then precmd_functions=( _direnv_hook ${precmd_functions[@]} ) fi typeset -ag chpwd_functions; if [[ -z ${chpwd_functions[(r)_direnv_hook]} ]]; then chpwd_functions=( _direnv_hook ${chpwd_functions[@]} ) fi ` func (sh zsh) Hook() (string, error) { return zshHook, nil } func (sh zsh) Export(e ShellExport) (out string) { for key, value := range e { if value == nil { out += sh.unset(key) } else { out += sh.export(key, *value) } } return out } func (sh zsh) Dump(env Env) (out string) { for key, value := range env { out += sh.export(key, value) } return out } func (sh zsh) export(key, value string) string { return "export " + sh.escape(key) + "=" + sh.escape(value) + ";" } func (sh zsh) unset(key string) string { return "unset " + sh.escape(key) + ";" } func (sh zsh) escape(str string) string { return BashEscape(str) } direnv-2.21.2/stdlib.go000066400000000000000000000651171361411000000146710ustar00rootroot00000000000000package main // StdLib ... const StdLib = "#!/usr/bin/env bash\n" + "#\n" + "# These are the commands available in an .envrc context\n" + "#\n" + "# ShellCheck exceptions:\n" + "#\n" + "# SC1090: Can't follow non-constant source. Use a directive to specify location.\n" + "# SC1091: Not following: (file missing)\n" + "# SC1117: Backslash is literal in \"\\n\". Prefer explicit escaping: \"\\\\n\".\n" + "# SC2059: Don't use variables in the printf format string. Use printf \"..%s..\" \"$foo\".\n" + "shopt -s gnu_errfmt\n" + "shopt -s nullglob\n" + "\n" + "\n" + "# NOTE: don't touch the RHS, it gets replaced at runtime\n" + "direnv=\"$(command -v direnv)\"\n" + "\n" + "# Config, change in the direnvrc\n" + "DIRENV_LOG_FORMAT=\"${DIRENV_LOG_FORMAT-direnv: %s}\"\n" + "\n" + "# Where direnv configuration should be stored\n" + "direnv_config_dir=${XDG_CONFIG_DIR:-$HOME/.config}/direnv\n" + "\n" + "# This variable can be used by programs to detect when they are running inside\n" + "# of a .envrc evaluation context. It is ignored by the direnv diffing\n" + "# algorithm and so it won't be re-exported.\n" + "export DIRENV_IN_ENVRC=1\n" + "\n" + "# Usage: direnv_layout_dir\n" + "#\n" + "# Prints the folder path that direnv should use to store layout content.\n" + "# This needs to be a function as $PWD might change during source_env/up.\n" + "#\n" + "# The output defaults to $PWD/.direnv.\n" + "\n" + "direnv_layout_dir() {\n" + " echo \"${direnv_layout_dir:-$PWD/.direnv}\"\n" + "}\n" + "\n" + "# Usage: log_status [ ...]\n" + "#\n" + "# Logs a status message. Acts like echo,\n" + "# but wraps output in the standard direnv log format\n" + "# (controlled by $DIRENV_LOG_FORMAT), and directs it\n" + "# to stderr rather than stdout.\n" + "#\n" + "# Example:\n" + "#\n" + "# log_status \"Loading ...\"\n" + "#\n" + "log_status() {\n" + " if [[ -n $DIRENV_LOG_FORMAT ]]; then\n" + " local msg=$*\n" + " # shellcheck disable=SC2059,SC1117\n" + " printf \"${DIRENV_LOG_FORMAT}\\n\" \"$msg\" >&2\n" + " fi\n" + "}\n" + "\n" + "# Usage: log_error [ ...]\n" + "#\n" + "# Logs an error message. Acts like echo,\n" + "# but wraps output in the standard direnv log format\n" + "# (controlled by $DIRENV_LOG_FORMAT), and directs it\n" + "# to stderr rather than stdout.\n" + "#\n" + "# Example:\n" + "#\n" + "# log_error \"Unable to find specified directory!\"\n" + "\n" + "log_error() {\n" + " local color_normal\n" + " local color_error\n" + " color_normal=$(tput sgr0)\n" + " color_error=$(tput setaf 1)\n" + " if [[ -n $DIRENV_LOG_FORMAT ]]; then\n" + " local msg=$*\n" + " # shellcheck disable=SC2059,SC1117\n" + " printf \"${color_error}${DIRENV_LOG_FORMAT}${color_normal}\\n\" \"$msg\" >&2\n" + " fi\n" + "}\n" + "\n" + "# Usage: has \n" + "#\n" + "# Returns 0 if the is available. Returns 1 otherwise. It can be a\n" + "# binary in the PATH or a shell function.\n" + "#\n" + "# Example:\n" + "#\n" + "# if has curl; then\n" + "# echo \"Yes we do\"\n" + "# fi\n" + "#\n" + "has() {\n" + " type \"$1\" &>/dev/null\n" + "}\n" + "\n" + "# Usage: join_args [args...]\n" + "#\n" + "# Joins all the passed arguments into a single string that can be evaluated by bash\n" + "#\n" + "# This is useful when one has to serialize an array of arguments back into a string\n" + "join_args() {\n" + " printf '%q ' \"$@\"\n" + "}\n" + "\n" + "# Usage: expand_path []\n" + "#\n" + "# Outputs the absolute path of relative to or the\n" + "# current directory.\n" + "#\n" + "# Example:\n" + "#\n" + "# cd /usr/local/games\n" + "# expand_path ../foo\n" + "# # output: /usr/local/foo\n" + "#\n" + "expand_path() {\n" + " \"$direnv\" expand_path \"$@\"\n" + "}\n" + "\n" + "# Usage: dotenv []\n" + "#\n" + "# Loads a \".env\" file into the current environment\n" + "#\n" + "dotenv() {\n" + " local path=${1:-}\n" + " if [[ -z $path ]]; then\n" + " path=$PWD/.env\n" + " elif [[ -d $path ]]; then\n" + " path=$path/.env\n" + " fi\n" + " if ! [[ -f $path ]]; then\n" + " log_error \".env at $path not found\"\n" + " return 1\n" + " fi\n" + " eval \"$(\"$direnv\" dotenv bash \"$@\")\"\n" + " watch_file \"$path\"\n" + "}\n" + "\n" + "# Usage: user_rel_path \n" + "#\n" + "# Transforms an absolute path into a user-relative path if\n" + "# possible.\n" + "#\n" + "# Example:\n" + "#\n" + "# echo $HOME\n" + "# # output: /home/user\n" + "# user_rel_path /home/user/my/project\n" + "# # output: ~/my/project\n" + "# user_rel_path /usr/local/lib\n" + "# # output: /usr/local/lib\n" + "#\n" + "user_rel_path() {\n" + " local abs_path=${1#-}\n" + "\n" + " if [[ -z $abs_path ]]; then return; fi\n" + "\n" + " if [[ -n $HOME ]]; then\n" + " local rel_path=${abs_path#$HOME}\n" + " if [[ $rel_path != \"$abs_path\" ]]; then\n" + " abs_path=~$rel_path\n" + " fi\n" + " fi\n" + "\n" + " echo \"$abs_path\"\n" + "}\n" + "\n" + "# Usage: find_up \n" + "#\n" + "# Outputs the path of when searched from the current directory up to\n" + "# /. Returns 1 if the file has not been found.\n" + "#\n" + "# Example:\n" + "#\n" + "# cd /usr/local/my\n" + "# mkdir -p project/foo\n" + "# touch bar\n" + "# cd project/foo\n" + "# find_up bar\n" + "# # output: /usr/local/my/bar\n" + "#\n" + "find_up() {\n" + " (\n" + " while true; do\n" + " if [[ -f $1 ]]; then\n" + " echo \"$PWD/$1\"\n" + " return 0\n" + " fi\n" + " if [[ $PWD == / ]] || [[ $PWD == // ]]; then\n" + " return 1\n" + " fi\n" + " cd ..\n" + " done\n" + " )\n" + "}\n" + "\n" + "# Usage: source_env \n" + "#\n" + "# Loads another \".envrc\" either by specifying its path or filename.\n" + "#\n" + "# NOTE: the other \".envrc\" is not checked by the security framework.\n" + "source_env() {\n" + " local rcpath=${1/#\\~/$HOME}\n" + " local rcfile\n" + " if [[ -d $rcpath ]]; then\n" + " rcpath=$rcpath/.envrc\n" + " fi\n" + " if [[ ! -e $rcpath ]]; then\n" + " log_status \"referenced $rcpath does not exist\"\n" + " return 1\n" + " fi\n" + "\n" + " rcfile=$(user_rel_path \"$rcpath\")\n" + " watch_file \"$rcpath\"\n" + "\n" + " pushd \"$(pwd 2>/dev/null)\" >/dev/null\n" + " pushd \"$(dirname \"$rcpath\")\" >/dev/null\n" + " if [[ -f ./$(basename \"$rcpath\") ]]; then\n" + " log_status \"loading $rcfile\"\n" + " # shellcheck disable=SC1090\n" + " . \"./$(basename \"$rcpath\")\"\n" + " else\n" + " log_status \"referenced $rcfile does not exist\"\n" + " fi\n" + " popd >/dev/null\n" + " popd >/dev/null\n" + "}\n" + "\n" + "# Usage: watch_file [ ...]\n" + "#\n" + "# Adds each to the list of files that direnv will watch for changes -\n" + "# useful when the contents of a file influence how variables are set -\n" + "# especially in direnvrc\n" + "#\n" + "watch_file() {\n" + " eval \"$(\"$direnv\" watch bash \"$@\")\"\n" + "}\n" + "\n" + "# Usage: source_up []\n" + "#\n" + "# Loads another \".envrc\" if found with the find_up command.\n" + "#\n" + "# NOTE: the other \".envrc\" is not checked by the security framework.\n" + "source_up() {\n" + " local dir file=${1:-.envrc}\n" + " dir=$(cd .. && find_up \"$file\")\n" + " if [[ -n $dir ]]; then\n" + " source_env \"$dir\"\n" + " fi\n" + "}\n" + "\n" + "# Usage: direnv_load \n" + "# e.g: direnv_load opam-env exec -- \"$direnv\" dump\n" + "#\n" + "# Applies the environment generated by running as a\n" + "# command. This is useful for adopting the environment of a child\n" + "# process - cause that process to run \"direnv dump\" and then wrap\n" + "# the results with direnv_load.\n" + "#\n" + "direnv_load() {\n" + " # Backup watches in case of `nix-shell --pure`\n" + " local prev_watches=$DIRENV_WATCHES\n" + " local prev_dump_file_path=${DIRENV_DUMP_FILE_PATH:-}\n" + "\n" + " # Create pipe\n" + " DIRENV_DUMP_FILE_PATH=$(mktemp -u)\n" + " export DIRENV_DUMP_FILE_PATH\n" + " mkfifo \"$DIRENV_DUMP_FILE_PATH\"\n" + "\n" + " # Run program in the background\n" + " (\"$@\")&\n" + "\n" + " # Apply the output of the dump\n" + " local exports\n" + " exports=$(\"$direnv\" apply_dump \"$DIRENV_DUMP_FILE_PATH\")\n" + " local es=$?\n" + "\n" + " # Regroup\n" + " rm \"$DIRENV_DUMP_FILE_PATH\"\n" + " wait # wait on the child process to exit\n" + " local es2=$?\n" + "\n" + " if [[ $es -ne 0 ]]; then\n" + " return $es\n" + " fi\n" + "\n" + " if [[ $es2 -ne 0 ]]; then\n" + " return $es2\n" + " fi\n" + "\n" + " eval \"$exports\"\n" + "\n" + " # Restore watches if the dump wiped them\n" + " if [[ -z \"$DIRENV_WATCHES\" ]]; then\n" + " export DIRENV_WATCHES=$prev_watches\n" + " fi\n" + " # Allow nesting\n" + " if [[ -n \"$prev_dump_file_path\" ]]; then\n" + " export DIRENV_DUMP_FILE_PATH=$prev_dump_file_path\n" + " else\n" + " unset DIRENV_DUMP_FILE_PATH\n" + " fi\n" + "}\n" + "\n" + "# Usage: PATH_add [ ...]\n" + "#\n" + "# Prepends the expanded to the PATH environment variable, in order.\n" + "# It prevents a common mistake where PATH is replaced by only the new ,\n" + "# or where a trailing colon is left in PATH, resulting in the current directory\n" + "# being considered in the PATH. Supports adding multiple directories at once.\n" + "#\n" + "# Example:\n" + "#\n" + "# pwd\n" + "# # output: /my/project\n" + "# PATH_add bin\n" + "# echo $PATH\n" + "# # output: /my/project/bin:/usr/bin:/bin\n" + "# PATH_add bam boum\n" + "# echo $PATH\n" + "# # output: /my/project/bam:/my/project/boum:/my/project/bin:/usr/bin:/bin\n" + "#\n" + "PATH_add() {\n" + " path_add PATH \"$@\"\n" + "}\n" + "\n" + "# Usage: path_add [ ...]\n" + "#\n" + "# Works like PATH_add except that it's for an arbitrary .\n" + "path_add() {\n" + " local path i var_name=\"$1\"\n" + " # split existing paths into an array\n" + " declare -a path_array\n" + " IFS=: read -ra path_array <<<\"${!1}\"\n" + " shift\n" + "\n" + " # prepend the passed paths in the right order\n" + " for ((i = $#; i > 0; i--)); do\n" + " path_array=(\"$(expand_path \"${!i}\")\" \"${path_array[@]}\")\n" + " done\n" + "\n" + " # join back all the paths\n" + " path=$(\n" + " IFS=:\n" + " echo \"${path_array[*]}\"\n" + " )\n" + "\n" + " # and finally export back the result to the original variable\n" + " export \"$var_name=$path\"\n" + "}\n" + "\n" + "# Usage: MANPATH_add \n" + "#\n" + "# Prepends a path to the MANPATH environment variable while making sure that\n" + "# `man` can still lookup the system manual pages.\n" + "#\n" + "# If MANPATH is not empty, man will only look in MANPATH.\n" + "# So if we set MANPATH=$path, man will only look in $path.\n" + "# Instead, prepend to `man -w` (which outputs man's default paths).\n" + "#\n" + "MANPATH_add() {\n" + " local old_paths=\"${MANPATH:-$(man -w)}\"\n" + " local dir\n" + " dir=$(expand_path \"$1\")\n" + " export \"MANPATH=$dir:$old_paths\"\n" + "}\n" + "\n" + "# Usage: load_prefix \n" + "#\n" + "# Expands some common path variables for the given prefix. This is\n" + "# useful if you installed something in the using\n" + "# $(./configure --prefix= && make install) and want to use it in\n" + "# the project.\n" + "#\n" + "# Variables set:\n" + "#\n" + "# CPATH\n" + "# LD_LIBRARY_PATH\n" + "# LIBRARY_PATH\n" + "# MANPATH\n" + "# PATH\n" + "# PKG_CONFIG_PATH\n" + "#\n" + "# Example:\n" + "#\n" + "# ./configure --prefix=$HOME/rubies/ruby-1.9.3\n" + "# make && make install\n" + "# # Then in the .envrc\n" + "# load_prefix ~/rubies/ruby-1.9.3\n" + "#\n" + "load_prefix() {\n" + " local dir\n" + " dir=$(expand_path \"$1\")\n" + " MANPATH_add \"$dir/man\"\n" + " MANPATH_add \"$dir/share/man\"\n" + " path_add CPATH \"$dir/include\"\n" + " path_add LD_LIBRARY_PATH \"$dir/lib\"\n" + " path_add LIBRARY_PATH \"$dir/lib\"\n" + " path_add PATH \"$dir/bin\"\n" + " path_add PKG_CONFIG_PATH \"$dir/lib/pkgconfig\"\n" + "}\n" + "\n" + "# Usage: layout \n" + "#\n" + "# A semantic dispatch used to describe common project layouts.\n" + "#\n" + "layout() {\n" + " local name=$1\n" + " shift\n" + " eval \"layout_$name\" \"$@\"\n" + "}\n" + "\n" + "# Usage: layout go\n" + "#\n" + "# Sets the GOPATH environment variable to the current directory.\n" + "#\n" + "layout_go() {\n" + " path_add GOPATH \"$PWD\"\n" + " PATH_add bin\n" + "}\n" + "\n" + "# Usage: layout node\n" + "#\n" + "# Adds \"$PWD/node_modules/.bin\" to the PATH environment variable.\n" + "layout_node() {\n" + " PATH_add node_modules/.bin\n" + "}\n" + "\n" + "# Usage: layout perl\n" + "#\n" + "# Setup environment variables required by perl's local::lib\n" + "# See http://search.cpan.org/dist/local-lib/lib/local/lib.pm for more details\n" + "#\n" + "layout_perl() {\n" + " local libdir\n" + " libdir=$(direnv_layout_dir)/perl5\n" + " export LOCAL_LIB_DIR=$libdir\n" + " export PERL_MB_OPT=\"--install_base '$libdir'\"\n" + " export PERL_MM_OPT=\"INSTALL_BASE=$libdir\"\n" + " path_add PERL5LIB \"$libdir/lib/perl5\"\n" + " path_add PERL_LOCAL_LIB_ROOT \"$libdir\"\n" + " PATH_add \"$libdir/bin\"\n" + "}\n" + "\n" + "# Usage: layout php\n" + "#\n" + "# Adds \"$PWD/vendor/bin\" to the PATH environment variable\n" + "layout_php() {\n" + " PATH_add vendor/bin\n" + "}\n" + "\n" + "# Usage: layout python \n" + "#\n" + "# Creates and loads a virtual environment under\n" + "# \"$direnv_layout_dir/python-$python_version\".\n" + "# This forces the installation of any egg into the project's sub-folder.\n" + "# For python older then 3.3 this requires virtualenv to be installed.\n" + "#\n" + "# It's possible to specify the python executable if you want to use different\n" + "# versions of python.\n" + "#\n" + "layout_python() {\n" + " local old_env\n" + " local python=${1:-python}\n" + " [[ $# -gt 0 ]] && shift\n" + " old_env=$(direnv_layout_dir)/virtualenv\n" + " unset PYTHONHOME\n" + " if [[ -d $old_env && $python == python ]]; then\n" + " VIRTUAL_ENV=$old_env\n" + " else\n" + " local python_version ve\n" + " # shellcheck disable=SC2046\n" + " read -r python_version ve <<<$($python -c \"import pkgutil as u, platform as p;ve='venv' if u.find_loader('venv') else ('virtualenv' if u.find_loader('virtualenv') else '');print(p.python_version()+' '+ve)\")\n" + " if [[ -z $python_version ]]; then\n" + " log_error \"Could not find python's version\"\n" + " return 1\n" + " fi\n" + "\n" + " VIRTUAL_ENV=$(direnv_layout_dir)/python-$python_version\n" + " case $ve in\n" + " \"venv\")\n" + " if [[ ! -d $VIRTUAL_ENV ]]; then\n" + " $python -m venv \"$@\" \"$VIRTUAL_ENV\"\n" + " fi\n" + " ;;\n" + " \"virtualenv\")\n" + " if [[ ! -d $VIRTUAL_ENV ]]; then\n" + " $python -m virtualenv \"$@\" \"$VIRTUAL_ENV\"\n" + " fi\n" + " ;;\n" + " *)\n" + " log_error \"Error: neither venv nor virtualenv are available.\"\n" + " return 1\n" + " ;;\n" + " esac\n" + " fi\n" + " export VIRTUAL_ENV\n" + " PATH_add \"$VIRTUAL_ENV/bin\"\n" + "}\n" + "\n" + "# Usage: layout python2\n" + "#\n" + "# A shortcut for $(layout python python2)\n" + "#\n" + "layout_python2() {\n" + " layout_python python2 \"$@\"\n" + "}\n" + "\n" + "# Usage: layout python3\n" + "#\n" + "# A shortcut for $(layout python python3)\n" + "#\n" + "layout_python3() {\n" + " layout_python python3 \"$@\"\n" + "}\n" + "\n" + "# Usage: layout anaconda []\n" + "#\n" + "# Activates anaconda for the named environment. If the environment\n" + "# hasn't been created, it will be using the environment.yml file in\n" + "# the current directory. is optional and will default to\n" + "# the one found in the system environment.\n" + "#\n" + "layout_anaconda() {\n" + " local env_name=$1\n" + " local env_loc\n" + " local conda\n" + " if [[ $# -gt 1 ]]; then\n" + " conda=${2}\n" + " else\n" + " conda=$(command -v conda)\n" + " fi\n" + " PATH_add \"$(dirname \"$conda\")\"\n" + " env_loc=$(\"$conda\" env list | grep -- \"^$env_name\\s\")\n" + " if [[ ! \"$env_loc\" == $env_name*$env_name ]]; then\n" + " if [[ -e environment.yml ]]; then\n" + " log_status \"creating conda environment\"\n" + " \"$conda\" env create\n" + " else\n" + " log_error \"Could not find environment.yml\"\n" + " return 1\n" + " fi\n" + " fi\n" + "\n" + " # shellcheck disable=SC1091\n" + " source activate \"$env_name\"\n" + "}\n" + "\n" + "# Usage: layout pipenv\n" + "#\n" + "# Similar to layout_python, but uses Pipenv to build a\n" + "# virtualenv from the Pipfile located in the same directory.\n" + "#\n" + "layout_pipenv() {\n" + " PIPENV_PIPFILE=\"${PIPENV_PIPFILE:-Pipfile}\"\n" + " if [[ ! -f \"$PIPENV_PIPFILE\" ]]; then\n" + " log_error \"No Pipfile found. Use \\`pipenv\\` to create a \\`$PIPENV_PIPFILE\\` first.\"\n" + " exit 2\n" + " fi\n" + "\n" + " VIRTUAL_ENV=$(pipenv --venv 2>/dev/null ; true)\n" + "\n" + " if [[ -z $VIRTUAL_ENV || ! -d $VIRTUAL_ENV ]]; then\n" + " pipenv install --dev\n" + " VIRTUAL_ENV=$(pipenv --venv)\n" + " fi\n" + "\n" + " PATH_add \"$VIRTUAL_ENV/bin\"\n" + " export PIPENV_ACTIVE=1\n" + " export VIRTUAL_ENV\n" + "}\n" + "\n" + "# Usage: layout pyenv [ ...]\n" + "#\n" + "# Example:\n" + "#\n" + "# layout pyenv 3.6.7\n" + "#\n" + "# Uses pyenv and layout_python to create and load a virtual environment under\n" + "# \"$direnv_layout_dir/python-$python_version\".\n" + "#\n" + "layout_pyenv() {\n" + " unset PYENV_VERSION\n" + " # layout_python prepends each python version to the PATH, so we add each\n" + " # version in reverse order so that the first listed version ends up\n" + " # first in the path\n" + " local i\n" + " for ((i = $#; i > 0; i--)); do\n" + " local python_version=${!i}\n" + " local pyenv_python\n" + " pyenv_python=$(pyenv root)/versions/${python_version}/bin/python\n" + " if [[ -x \"$pyenv_python\" ]]; then\n" + " if layout_python \"$pyenv_python\"; then\n" + " # e.g. Given \"use pyenv 3.6.9 2.7.16\", PYENV_VERSION becomes \"3.6.9:2.7.16\"\n" + " PYENV_VERSION=${python_version}${PYENV_VERSION:+:$PYENV_VERSION}\n" + " fi\n" + " else\n" + " log_error \"pyenv: version '$python_version' not installed\"\n" + " return 1\n" + " fi\n" + " done\n" + "\n" + " [[ -n \"$PYENV_VERSION\" ]] && export PYENV_VERSION\n" + "}\n" + "\n" + "# Usage: layout ruby\n" + "#\n" + "# Sets the GEM_HOME environment variable to \"$(direnv_layout_dir)/ruby/RUBY_VERSION\".\n" + "# This forces the installation of any gems into the project's sub-folder.\n" + "# If you're using bundler it will create wrapper programs that can be invoked\n" + "# directly instead of using the $(bundle exec) prefix.\n" + "#\n" + "layout_ruby() {\n" + " BUNDLE_BIN=$(direnv_layout_dir)/bin\n" + "\n" + " if ruby -e \"exit Gem::VERSION > '2.2.0'\" 2>/dev/null; then\n" + " GEM_HOME=$(direnv_layout_dir)/ruby\n" + " else\n" + " local ruby_version\n" + " ruby_version=$(ruby -e\"puts (defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby') + '-' + RUBY_VERSION\")\n" + " GEM_HOME=$(direnv_layout_dir)/ruby-${ruby_version}\n" + " fi\n" + "\n" + " export BUNDLE_BIN\n" + " export GEM_HOME\n" + "\n" + " PATH_add \"$GEM_HOME/bin\"\n" + " PATH_add \"$BUNDLE_BIN\"\n" + "}\n" + "\n" + "# Usage: layout julia\n" + "#\n" + "# Sets the JULIA_PROJECT environment variable to the current directory.\n" + "layout_julia() {\n" + " export JULIA_PROJECT=$PWD\n" + "}\n" + "\n" + "# Usage: use []\n" + "#\n" + "# A semantic command dispatch intended for loading external dependencies into\n" + "# the environment.\n" + "#\n" + "# Example:\n" + "#\n" + "# use_ruby() {\n" + "# echo \"Ruby $1\"\n" + "# }\n" + "# use ruby 1.9.3\n" + "# # output: Ruby 1.9.3\n" + "#\n" + "use() {\n" + " local cmd=$1\n" + " log_status \"using $*\"\n" + " shift\n" + " \"use_$cmd\" \"$@\"\n" + "}\n" + "\n" + "# Usage: use rbenv\n" + "#\n" + "# Loads rbenv which add the ruby wrappers available on the PATH.\n" + "#\n" + "use_rbenv() {\n" + " eval \"$(rbenv init -)\"\n" + "}\n" + "\n" + "# Usage: rvm [...]\n" + "#\n" + "# Should work just like in the shell if you have rvm installed.#\n" + "#\n" + "rvm() {\n" + " unset rvm\n" + " if [[ -n ${rvm_scripts_path:-} ]]; then\n" + " # shellcheck disable=SC1090\n" + " source \"${rvm_scripts_path}/rvm\"\n" + " elif [[ -n ${rvm_path:-} ]]; then\n" + " # shellcheck disable=SC1090\n" + " source \"${rvm_path}/scripts/rvm\"\n" + " else\n" + " # shellcheck disable=SC1090\n" + " source \"$HOME/.rvm/scripts/rvm\"\n" + " fi\n" + " rvm \"$@\"\n" + "}\n" + "\n" + "# Usage: use node\n" + "# Loads NodeJS version from a `.node-version` or `.nvmrc` file.\n" + "#\n" + "# Usage: use node []\n" + "# Loads specified NodeJS version.\n" + "#\n" + "# If you specify a partial NodeJS version (i.e. `4.2`), a fuzzy match\n" + "# is performed and the highest matching version installed is selected.\n" + "#\n" + "# Environment Variables:\n" + "#\n" + "# - $NODE_VERSIONS (required)\n" + "# You must specify a path to your installed NodeJS versions via the `$NODE_VERSIONS` variable.\n" + "#\n" + "# - $NODE_VERSION_PREFIX (optional) [default=\"node-v\"]\n" + "# Overrides the default version prefix.\n" + "\n" + "use_node() {\n" + " local version=${1:-}\n" + " local via=\"\"\n" + " local node_version_prefix=${NODE_VERSION_PREFIX-node-v}\n" + " local node_wanted\n" + " local node_prefix\n" + "\n" + " if [[ -z ${NODE_VERSIONS:-} || ! -d $NODE_VERSIONS ]]; then\n" + " log_error \"You must specify a \\$NODE_VERSIONS environment variable and the directory specified must exist!\"\n" + " return 1\n" + " fi\n" + "\n" + " if [[ -z $version && -f .nvmrc ]]; then\n" + " version=$(<.nvmrc)\n" + " via=\".nvmrc\"\n" + " fi\n" + "\n" + " if [[ -z $version && -f .node-version ]]; then\n" + " version=$(<.node-version)\n" + " via=\".node-version\"\n" + " fi\n" + "\n" + " if [[ -z $version ]]; then\n" + " log_error \"I do not know which NodeJS version to load because one has not been specified!\"\n" + " return 1\n" + " fi\n" + "\n" + " node_wanted=${node_version_prefix}${version}\n" + " node_prefix=$(\n" + " # Look for matching node versions in $NODE_VERSIONS path\n" + " # Strip possible \"/\" suffix from $NODE_VERSIONS, then use that to\n" + " # Strip $NODE_VERSIONS/$NODE_VERSION_PREFIX prefix from line.\n" + " # Sort by version: split by \".\" then reverse numeric sort for each piece of the version string\n" + " # The first one is the highest\n" + " find \"$NODE_VERSIONS\" -maxdepth 1 -mindepth 1 -type d -name \"$node_wanted*\" \\\n" + " | while IFS= read -r line; do echo \"${line#${NODE_VERSIONS%/}/${node_version_prefix}}\"; done \\\n" + " | sort -t . -k 1,1rn -k 2,2rn -k 3,3rn \\\n" + " | head -1\n" + " )\n" + "\n" + " node_prefix=\"${NODE_VERSIONS}/${node_version_prefix}${node_prefix}\"\n" + "\n" + " if [[ ! -d $node_prefix ]]; then\n" + " log_error \"Unable to find NodeJS version ($version) in ($NODE_VERSIONS)!\"\n" + " return 1\n" + " fi\n" + "\n" + " if [[ ! -x $node_prefix/bin/node ]]; then\n" + " log_error \"Unable to load NodeJS binary (node) for version ($version) in ($NODE_VERSIONS)!\"\n" + " return 1\n" + " fi\n" + "\n" + " load_prefix \"$node_prefix\"\n" + "\n" + " if [[ -z $via ]]; then\n" + " log_status \"Successfully loaded NodeJS $(node --version), from prefix ($node_prefix)\"\n" + " else\n" + " log_status \"Successfully loaded NodeJS $(node --version) (via $via), from prefix ($node_prefix)\"\n" + " fi\n" + "}\n" + "\n" + "# Usage: use_nix [...]\n" + "#\n" + "# Load environment variables from `nix-shell`.\n" + "# If you have a `default.nix` or `shell.nix` these will be\n" + "# used by default, but you can also specify packages directly\n" + "# (e.g `use nix -p ocaml`).\n" + "#\n" + "use_nix() {\n" + " direnv_load nix-shell --show-trace \"$@\" --run \"$(join_args \"$direnv\" dump)\"\n" + " if [[ $# == 0 ]]; then\n" + " watch_file default.nix shell.nix\n" + " fi\n" + "}\n" + "\n" + "# Usage: use_guix [...]\n" + "#\n" + "# Load environment variables from `guix environment`.\n" + "# Any arguments given will be passed to guix environment. For example,\n" + "# `use guix hello` would setup an environment with the dependencies of\n" + "# the hello package. To create an environment including hello, the\n" + "# `--ad-hoc` flag is used `use guix --ad-hoc hello`. Other options\n" + "# include `--load` which allows loading an environment from a\n" + "# file. For a full list of options, consult the documentation for the\n" + "# `guix environment` command.\n" + "use_guix() {\n" + " eval \"$(guix environment \"$@\" --search-paths)\"\n" + "}\n" + "\n" + "# Usage: direnv_version \n" + "#\n" + "# Checks that the direnv version is at least old as .\n" + "direnv_version() {\n" + " \"$direnv\" version \"$@\"\n" + "}\n" + "\n" + "# Usage: __main__ [...]\n" + "#\n" + "# Used by rc.go\n" + "__main__() {\n" + " # reserve stdout for dumping\n" + " exec 3>&1\n" + " exec 1>&2\n" + "\n" + " __dump_at_exit() {\n" + " local ret=$?\n" + " \"$direnv\" dump json 3\n" + " trap - EXIT\n" + " exit \"$ret\"\n" + " }\n" + " trap __dump_at_exit EXIT\n" + "\n" + " # load direnv libraries\n" + " for lib in \"$direnv_config_dir/lib/\"*.sh; do\n" + " # shellcheck disable=SC1090\n" + " source \"$lib\"\n" + " done\n" + "\n" + " # load the global ~/.direnvrc if present\n" + " if [[ -f $direnv_config_dir/direnvrc ]]; then\n" + " # shellcheck disable=SC1090\n" + " source \"$direnv_config_dir/direnvrc\" >&2\n" + " elif [[ -f $HOME/.direnvrc ]]; then\n" + " # shellcheck disable=SC1090\n" + " source \"$HOME/.direnvrc\" >&2\n" + " fi\n" + "\n" + " # and finally load the .envrc\n" + " \"$@\"\n" + "}\n" + "" direnv-2.21.2/stdlib.sh000077500000000000000000000510121361411000000146660ustar00rootroot00000000000000#!/usr/bin/env bash # # These are the commands available in an .envrc context # # ShellCheck exceptions: # # SC1090: Can't follow non-constant source. Use a directive to specify location. # SC1091: Not following: (file missing) # SC1117: Backslash is literal in "\n". Prefer explicit escaping: "\\n". # SC2059: Don't use variables in the printf format string. Use printf "..%s.." "$foo". shopt -s gnu_errfmt shopt -s nullglob # NOTE: don't touch the RHS, it gets replaced at runtime direnv="$(command -v direnv)" # Config, change in the direnvrc DIRENV_LOG_FORMAT="${DIRENV_LOG_FORMAT-direnv: %s}" # Where direnv configuration should be stored direnv_config_dir=${XDG_CONFIG_DIR:-$HOME/.config}/direnv # This variable can be used by programs to detect when they are running inside # of a .envrc evaluation context. It is ignored by the direnv diffing # algorithm and so it won't be re-exported. export DIRENV_IN_ENVRC=1 # Usage: direnv_layout_dir # # Prints the folder path that direnv should use to store layout content. # This needs to be a function as $PWD might change during source_env/up. # # The output defaults to $PWD/.direnv. direnv_layout_dir() { echo "${direnv_layout_dir:-$PWD/.direnv}" } # Usage: log_status [ ...] # # Logs a status message. Acts like echo, # but wraps output in the standard direnv log format # (controlled by $DIRENV_LOG_FORMAT), and directs it # to stderr rather than stdout. # # Example: # # log_status "Loading ..." # log_status() { if [[ -n $DIRENV_LOG_FORMAT ]]; then local msg=$* # shellcheck disable=SC2059,SC1117 printf "${DIRENV_LOG_FORMAT}\n" "$msg" >&2 fi } # Usage: log_error [ ...] # # Logs an error message. Acts like echo, # but wraps output in the standard direnv log format # (controlled by $DIRENV_LOG_FORMAT), and directs it # to stderr rather than stdout. # # Example: # # log_error "Unable to find specified directory!" log_error() { local color_normal local color_error color_normal=$(tput sgr0) color_error=$(tput setaf 1) if [[ -n $DIRENV_LOG_FORMAT ]]; then local msg=$* # shellcheck disable=SC2059,SC1117 printf "${color_error}${DIRENV_LOG_FORMAT}${color_normal}\n" "$msg" >&2 fi } # Usage: has # # Returns 0 if the is available. Returns 1 otherwise. It can be a # binary in the PATH or a shell function. # # Example: # # if has curl; then # echo "Yes we do" # fi # has() { type "$1" &>/dev/null } # Usage: join_args [args...] # # Joins all the passed arguments into a single string that can be evaluated by bash # # This is useful when one has to serialize an array of arguments back into a string join_args() { printf '%q ' "$@" } # Usage: expand_path [] # # Outputs the absolute path of relative to or the # current directory. # # Example: # # cd /usr/local/games # expand_path ../foo # # output: /usr/local/foo # expand_path() { "$direnv" expand_path "$@" } # Usage: dotenv [] # # Loads a ".env" file into the current environment # dotenv() { local path=${1:-} if [[ -z $path ]]; then path=$PWD/.env elif [[ -d $path ]]; then path=$path/.env fi if ! [[ -f $path ]]; then log_error ".env at $path not found" return 1 fi eval "$("$direnv" dotenv bash "$@")" watch_file "$path" } # Usage: user_rel_path # # Transforms an absolute path into a user-relative path if # possible. # # Example: # # echo $HOME # # output: /home/user # user_rel_path /home/user/my/project # # output: ~/my/project # user_rel_path /usr/local/lib # # output: /usr/local/lib # user_rel_path() { local abs_path=${1#-} if [[ -z $abs_path ]]; then return; fi if [[ -n $HOME ]]; then local rel_path=${abs_path#$HOME} if [[ $rel_path != "$abs_path" ]]; then abs_path=~$rel_path fi fi echo "$abs_path" } # Usage: find_up # # Outputs the path of when searched from the current directory up to # /. Returns 1 if the file has not been found. # # Example: # # cd /usr/local/my # mkdir -p project/foo # touch bar # cd project/foo # find_up bar # # output: /usr/local/my/bar # find_up() { ( while true; do if [[ -f $1 ]]; then echo "$PWD/$1" return 0 fi if [[ $PWD == / ]] || [[ $PWD == // ]]; then return 1 fi cd .. done ) } # Usage: source_env # # Loads another ".envrc" either by specifying its path or filename. # # NOTE: the other ".envrc" is not checked by the security framework. source_env() { local rcpath=${1/#\~/$HOME} local rcfile if [[ -d $rcpath ]]; then rcpath=$rcpath/.envrc fi if [[ ! -e $rcpath ]]; then log_status "referenced $rcpath does not exist" return 1 fi rcfile=$(user_rel_path "$rcpath") watch_file "$rcpath" pushd "$(pwd 2>/dev/null)" >/dev/null pushd "$(dirname "$rcpath")" >/dev/null if [[ -f ./$(basename "$rcpath") ]]; then log_status "loading $rcfile" # shellcheck disable=SC1090 . "./$(basename "$rcpath")" else log_status "referenced $rcfile does not exist" fi popd >/dev/null popd >/dev/null } # Usage: watch_file [ ...] # # Adds each to the list of files that direnv will watch for changes - # useful when the contents of a file influence how variables are set - # especially in direnvrc # watch_file() { eval "$("$direnv" watch bash "$@")" } # Usage: source_up [] # # Loads another ".envrc" if found with the find_up command. # # NOTE: the other ".envrc" is not checked by the security framework. source_up() { local dir file=${1:-.envrc} dir=$(cd .. && find_up "$file") if [[ -n $dir ]]; then source_env "$dir" fi } # Usage: direnv_load # e.g: direnv_load opam-env exec -- "$direnv" dump # # Applies the environment generated by running as a # command. This is useful for adopting the environment of a child # process - cause that process to run "direnv dump" and then wrap # the results with direnv_load. # direnv_load() { # Backup watches in case of `nix-shell --pure` local prev_watches=$DIRENV_WATCHES local prev_dump_file_path=${DIRENV_DUMP_FILE_PATH:-} # Create pipe DIRENV_DUMP_FILE_PATH=$(mktemp -u) export DIRENV_DUMP_FILE_PATH mkfifo "$DIRENV_DUMP_FILE_PATH" # Run program in the background ("$@")& # Apply the output of the dump local exports exports=$("$direnv" apply_dump "$DIRENV_DUMP_FILE_PATH") local es=$? # Regroup rm "$DIRENV_DUMP_FILE_PATH" wait # wait on the child process to exit local es2=$? if [[ $es -ne 0 ]]; then return $es fi if [[ $es2 -ne 0 ]]; then return $es2 fi eval "$exports" # Restore watches if the dump wiped them if [[ -z "$DIRENV_WATCHES" ]]; then export DIRENV_WATCHES=$prev_watches fi # Allow nesting if [[ -n "$prev_dump_file_path" ]]; then export DIRENV_DUMP_FILE_PATH=$prev_dump_file_path else unset DIRENV_DUMP_FILE_PATH fi } # Usage: PATH_add [ ...] # # Prepends the expanded to the PATH environment variable, in order. # It prevents a common mistake where PATH is replaced by only the new , # or where a trailing colon is left in PATH, resulting in the current directory # being considered in the PATH. Supports adding multiple directories at once. # # Example: # # pwd # # output: /my/project # PATH_add bin # echo $PATH # # output: /my/project/bin:/usr/bin:/bin # PATH_add bam boum # echo $PATH # # output: /my/project/bam:/my/project/boum:/my/project/bin:/usr/bin:/bin # PATH_add() { path_add PATH "$@" } # Usage: path_add [ ...] # # Works like PATH_add except that it's for an arbitrary . path_add() { local path i var_name="$1" # split existing paths into an array declare -a path_array IFS=: read -ra path_array <<<"${!1}" shift # prepend the passed paths in the right order for ((i = $#; i > 0; i--)); do path_array=("$(expand_path "${!i}")" "${path_array[@]}") done # join back all the paths path=$( IFS=: echo "${path_array[*]}" ) # and finally export back the result to the original variable export "$var_name=$path" } # Usage: MANPATH_add # # Prepends a path to the MANPATH environment variable while making sure that # `man` can still lookup the system manual pages. # # If MANPATH is not empty, man will only look in MANPATH. # So if we set MANPATH=$path, man will only look in $path. # Instead, prepend to `man -w` (which outputs man's default paths). # MANPATH_add() { local old_paths="${MANPATH:-$(man -w)}" local dir dir=$(expand_path "$1") export "MANPATH=$dir:$old_paths" } # Usage: load_prefix # # Expands some common path variables for the given prefix. This is # useful if you installed something in the using # $(./configure --prefix= && make install) and want to use it in # the project. # # Variables set: # # CPATH # LD_LIBRARY_PATH # LIBRARY_PATH # MANPATH # PATH # PKG_CONFIG_PATH # # Example: # # ./configure --prefix=$HOME/rubies/ruby-1.9.3 # make && make install # # Then in the .envrc # load_prefix ~/rubies/ruby-1.9.3 # load_prefix() { local dir dir=$(expand_path "$1") MANPATH_add "$dir/man" MANPATH_add "$dir/share/man" path_add CPATH "$dir/include" path_add LD_LIBRARY_PATH "$dir/lib" path_add LIBRARY_PATH "$dir/lib" path_add PATH "$dir/bin" path_add PKG_CONFIG_PATH "$dir/lib/pkgconfig" } # Usage: layout # # A semantic dispatch used to describe common project layouts. # layout() { local name=$1 shift eval "layout_$name" "$@" } # Usage: layout go # # Sets the GOPATH environment variable to the current directory. # layout_go() { path_add GOPATH "$PWD" PATH_add bin } # Usage: layout node # # Adds "$PWD/node_modules/.bin" to the PATH environment variable. layout_node() { PATH_add node_modules/.bin } # Usage: layout perl # # Setup environment variables required by perl's local::lib # See http://search.cpan.org/dist/local-lib/lib/local/lib.pm for more details # layout_perl() { local libdir libdir=$(direnv_layout_dir)/perl5 export LOCAL_LIB_DIR=$libdir export PERL_MB_OPT="--install_base '$libdir'" export PERL_MM_OPT="INSTALL_BASE=$libdir" path_add PERL5LIB "$libdir/lib/perl5" path_add PERL_LOCAL_LIB_ROOT "$libdir" PATH_add "$libdir/bin" } # Usage: layout php # # Adds "$PWD/vendor/bin" to the PATH environment variable layout_php() { PATH_add vendor/bin } # Usage: layout python # # Creates and loads a virtual environment under # "$direnv_layout_dir/python-$python_version". # This forces the installation of any egg into the project's sub-folder. # For python older then 3.3 this requires virtualenv to be installed. # # It's possible to specify the python executable if you want to use different # versions of python. # layout_python() { local old_env local python=${1:-python} [[ $# -gt 0 ]] && shift old_env=$(direnv_layout_dir)/virtualenv unset PYTHONHOME if [[ -d $old_env && $python == python ]]; then VIRTUAL_ENV=$old_env else local python_version ve # shellcheck disable=SC2046 read -r python_version ve <<<$($python -c "import pkgutil as u, platform as p;ve='venv' if u.find_loader('venv') else ('virtualenv' if u.find_loader('virtualenv') else '');print(p.python_version()+' '+ve)") if [[ -z $python_version ]]; then log_error "Could not find python's version" return 1 fi VIRTUAL_ENV=$(direnv_layout_dir)/python-$python_version case $ve in "venv") if [[ ! -d $VIRTUAL_ENV ]]; then $python -m venv "$@" "$VIRTUAL_ENV" fi ;; "virtualenv") if [[ ! -d $VIRTUAL_ENV ]]; then $python -m virtualenv "$@" "$VIRTUAL_ENV" fi ;; *) log_error "Error: neither venv nor virtualenv are available." return 1 ;; esac fi export VIRTUAL_ENV PATH_add "$VIRTUAL_ENV/bin" } # Usage: layout python2 # # A shortcut for $(layout python python2) # layout_python2() { layout_python python2 "$@" } # Usage: layout python3 # # A shortcut for $(layout python python3) # layout_python3() { layout_python python3 "$@" } # Usage: layout anaconda [] # # Activates anaconda for the named environment. If the environment # hasn't been created, it will be using the environment.yml file in # the current directory. is optional and will default to # the one found in the system environment. # layout_anaconda() { local env_name=$1 local env_loc local conda if [[ $# -gt 1 ]]; then conda=${2} else conda=$(command -v conda) fi PATH_add "$(dirname "$conda")" env_loc=$("$conda" env list | grep -- "^$env_name\s") if [[ ! "$env_loc" == $env_name*$env_name ]]; then if [[ -e environment.yml ]]; then log_status "creating conda environment" "$conda" env create else log_error "Could not find environment.yml" return 1 fi fi # shellcheck disable=SC1091 source activate "$env_name" } # Usage: layout pipenv # # Similar to layout_python, but uses Pipenv to build a # virtualenv from the Pipfile located in the same directory. # layout_pipenv() { PIPENV_PIPFILE="${PIPENV_PIPFILE:-Pipfile}" if [[ ! -f "$PIPENV_PIPFILE" ]]; then log_error "No Pipfile found. Use \`pipenv\` to create a \`$PIPENV_PIPFILE\` first." exit 2 fi VIRTUAL_ENV=$(pipenv --venv 2>/dev/null ; true) if [[ -z $VIRTUAL_ENV || ! -d $VIRTUAL_ENV ]]; then pipenv install --dev VIRTUAL_ENV=$(pipenv --venv) fi PATH_add "$VIRTUAL_ENV/bin" export PIPENV_ACTIVE=1 export VIRTUAL_ENV } # Usage: layout pyenv [ ...] # # Example: # # layout pyenv 3.6.7 # # Uses pyenv and layout_python to create and load a virtual environment under # "$direnv_layout_dir/python-$python_version". # layout_pyenv() { unset PYENV_VERSION # layout_python prepends each python version to the PATH, so we add each # version in reverse order so that the first listed version ends up # first in the path local i for ((i = $#; i > 0; i--)); do local python_version=${!i} local pyenv_python pyenv_python=$(pyenv root)/versions/${python_version}/bin/python if [[ -x "$pyenv_python" ]]; then if layout_python "$pyenv_python"; then # e.g. Given "use pyenv 3.6.9 2.7.16", PYENV_VERSION becomes "3.6.9:2.7.16" PYENV_VERSION=${python_version}${PYENV_VERSION:+:$PYENV_VERSION} fi else log_error "pyenv: version '$python_version' not installed" return 1 fi done [[ -n "$PYENV_VERSION" ]] && export PYENV_VERSION } # Usage: layout ruby # # Sets the GEM_HOME environment variable to "$(direnv_layout_dir)/ruby/RUBY_VERSION". # This forces the installation of any gems into the project's sub-folder. # If you're using bundler it will create wrapper programs that can be invoked # directly instead of using the $(bundle exec) prefix. # layout_ruby() { BUNDLE_BIN=$(direnv_layout_dir)/bin if ruby -e "exit Gem::VERSION > '2.2.0'" 2>/dev/null; then GEM_HOME=$(direnv_layout_dir)/ruby else local ruby_version ruby_version=$(ruby -e"puts (defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby') + '-' + RUBY_VERSION") GEM_HOME=$(direnv_layout_dir)/ruby-${ruby_version} fi export BUNDLE_BIN export GEM_HOME PATH_add "$GEM_HOME/bin" PATH_add "$BUNDLE_BIN" } # Usage: layout julia # # Sets the JULIA_PROJECT environment variable to the current directory. layout_julia() { export JULIA_PROJECT=$PWD } # Usage: use [] # # A semantic command dispatch intended for loading external dependencies into # the environment. # # Example: # # use_ruby() { # echo "Ruby $1" # } # use ruby 1.9.3 # # output: Ruby 1.9.3 # use() { local cmd=$1 log_status "using $*" shift "use_$cmd" "$@" } # Usage: use rbenv # # Loads rbenv which add the ruby wrappers available on the PATH. # use_rbenv() { eval "$(rbenv init -)" } # Usage: rvm [...] # # Should work just like in the shell if you have rvm installed.# # rvm() { unset rvm if [[ -n ${rvm_scripts_path:-} ]]; then # shellcheck disable=SC1090 source "${rvm_scripts_path}/rvm" elif [[ -n ${rvm_path:-} ]]; then # shellcheck disable=SC1090 source "${rvm_path}/scripts/rvm" else # shellcheck disable=SC1090 source "$HOME/.rvm/scripts/rvm" fi rvm "$@" } # Usage: use node # Loads NodeJS version from a `.node-version` or `.nvmrc` file. # # Usage: use node [] # Loads specified NodeJS version. # # If you specify a partial NodeJS version (i.e. `4.2`), a fuzzy match # is performed and the highest matching version installed is selected. # # Environment Variables: # # - $NODE_VERSIONS (required) # You must specify a path to your installed NodeJS versions via the `$NODE_VERSIONS` variable. # # - $NODE_VERSION_PREFIX (optional) [default="node-v"] # Overrides the default version prefix. use_node() { local version=${1:-} local via="" local node_version_prefix=${NODE_VERSION_PREFIX-node-v} local node_wanted local node_prefix if [[ -z ${NODE_VERSIONS:-} || ! -d $NODE_VERSIONS ]]; then log_error "You must specify a \$NODE_VERSIONS environment variable and the directory specified must exist!" return 1 fi if [[ -z $version && -f .nvmrc ]]; then version=$(<.nvmrc) via=".nvmrc" fi if [[ -z $version && -f .node-version ]]; then version=$(<.node-version) via=".node-version" fi if [[ -z $version ]]; then log_error "I do not know which NodeJS version to load because one has not been specified!" return 1 fi node_wanted=${node_version_prefix}${version} node_prefix=$( # Look for matching node versions in $NODE_VERSIONS path # Strip possible "/" suffix from $NODE_VERSIONS, then use that to # Strip $NODE_VERSIONS/$NODE_VERSION_PREFIX prefix from line. # Sort by version: split by "." then reverse numeric sort for each piece of the version string # The first one is the highest find "$NODE_VERSIONS" -maxdepth 1 -mindepth 1 -type d -name "$node_wanted*" \ | while IFS= read -r line; do echo "${line#${NODE_VERSIONS%/}/${node_version_prefix}}"; done \ | sort -t . -k 1,1rn -k 2,2rn -k 3,3rn \ | head -1 ) node_prefix="${NODE_VERSIONS}/${node_version_prefix}${node_prefix}" if [[ ! -d $node_prefix ]]; then log_error "Unable to find NodeJS version ($version) in ($NODE_VERSIONS)!" return 1 fi if [[ ! -x $node_prefix/bin/node ]]; then log_error "Unable to load NodeJS binary (node) for version ($version) in ($NODE_VERSIONS)!" return 1 fi load_prefix "$node_prefix" if [[ -z $via ]]; then log_status "Successfully loaded NodeJS $(node --version), from prefix ($node_prefix)" else log_status "Successfully loaded NodeJS $(node --version) (via $via), from prefix ($node_prefix)" fi } # Usage: use_nix [...] # # Load environment variables from `nix-shell`. # If you have a `default.nix` or `shell.nix` these will be # used by default, but you can also specify packages directly # (e.g `use nix -p ocaml`). # use_nix() { direnv_load nix-shell --show-trace "$@" --run "$(join_args "$direnv" dump)" if [[ $# == 0 ]]; then watch_file default.nix shell.nix fi } # Usage: use_guix [...] # # Load environment variables from `guix environment`. # Any arguments given will be passed to guix environment. For example, # `use guix hello` would setup an environment with the dependencies of # the hello package. To create an environment including hello, the # `--ad-hoc` flag is used `use guix --ad-hoc hello`. Other options # include `--load` which allows loading an environment from a # file. For a full list of options, consult the documentation for the # `guix environment` command. use_guix() { eval "$(guix environment "$@" --search-paths)" } # Usage: direnv_version # # Checks that the direnv version is at least old as . direnv_version() { "$direnv" version "$@" } # Usage: __main__ [...] # # Used by rc.go __main__() { # reserve stdout for dumping exec 3>&1 exec 1>&2 __dump_at_exit() { local ret=$? "$direnv" dump json 3 trap - EXIT exit "$ret" } trap __dump_at_exit EXIT # load direnv libraries for lib in "$direnv_config_dir/lib/"*.sh; do # shellcheck disable=SC1090 source "$lib" done # load the global ~/.direnvrc if present if [[ -f $direnv_config_dir/direnvrc ]]; then # shellcheck disable=SC1090 source "$direnv_config_dir/direnvrc" >&2 elif [[ -f $HOME/.direnvrc ]]; then # shellcheck disable=SC1090 source "$HOME/.direnvrc" >&2 fi # and finally load the .envrc "$@" } direnv-2.21.2/test/000077500000000000000000000000001361411000000140265ustar00rootroot00000000000000direnv-2.21.2/test/.envrc000066400000000000000000000000001361411000000151320ustar00rootroot00000000000000direnv-2.21.2/test/config/000077500000000000000000000000001361411000000152735ustar00rootroot00000000000000direnv-2.21.2/test/config/direnv/000077500000000000000000000000001361411000000165625ustar00rootroot00000000000000direnv-2.21.2/test/config/direnv/direnvrc000066400000000000000000000000001361411000000203070ustar00rootroot00000000000000direnv-2.21.2/test/direnv-test-common.sh000077500000000000000000000075601361411000000201270ustar00rootroot00000000000000# Test script for Bourne-shell extensions. Set TARGET_SHELL # to the shell to be tested (bash, zsh, etc) before sourcing it. if [[ -z "$TARGET_SHELL" ]]; then echo "TARGET_SHELL variable not set" exit 1 fi set -e cd "$(dirname "$0")" TEST_DIR=$PWD export XDG_CONFIG_HOME=${TEST_DIR}/config export XDG_DATA_HOME=${TEST_DIR}/data PATH=$(dirname "$TEST_DIR"):$PATH export PATH # Reset the direnv loading if any export DIRENV_CONFIG=$PWD unset DIRENV_BASH unset DIRENV_DIR unset DIRENV_MTIME unset DIRENV_WATCHES unset DIRENV_DIFF mkdir -p "${XDG_CONFIG_HOME}/direnv" touch "${XDG_CONFIG_HOME}/direnv/direnvrc" has() { type -P "$1" &>/dev/null } direnv_eval() { eval "$(direnv export "$TARGET_SHELL")" } test_start() { cd "$TEST_DIR/scenarios/$1" direnv allow if [[ "$DIRENV_DEBUG" == "1" ]]; then echo fi echo "## Testing $1 ##" if [[ "$DIRENV_DEBUG" == "1" ]]; then echo fi } test_stop() { cd "$TEST_DIR" direnv_eval } test_eq() { if [[ "$1" != "$2" ]]; then echo "FAILED: '$1' == '$2'" exit 1 fi } test_neq() { if [[ "$1" == "$2" ]]; then echo "FAILED: '$1' != '$2'" exit 1 fi } ### RUN ### direnv allow || true direnv_eval test_start base echo "Setting up" direnv_eval test_eq "$HELLO" "world" WATCHES=$DIRENV_WATCHES echo "Reloading (should be no-op)" direnv_eval test_eq "$WATCHES" "$DIRENV_WATCHES" sleep 1 echo "Updating envrc and reloading (should reload)" touch .envrc direnv_eval test_neq "$WATCHES" "$DIRENV_WATCHES" echo "Leaving dir (should clear env set by dir's envrc)" cd .. direnv_eval echo "${HELLO}" test -z "${HELLO}" test_stop test_start inherit cp ../base/.envrc ../inherited/.envrc direnv_eval echo "HELLO should be world:" "$HELLO" sleep 1 echo "export HELLO=goodbye" > ../inherited/.envrc direnv_eval test_eq "$HELLO" "goodbye" test_stop if has ruby; then test_start "ruby-layout" direnv_eval test_neq "$GEM_HOME" "" test_stop fi # Make sure directories with spaces are fine test_start "space dir" direnv_eval test_eq "$SPACE_DIR" "true" test_stop test_start "child-env" direnv_eval test_eq "$PARENT_PRE" "1" test_eq "$CHILD" "1" test_eq "$PARENT_POST" "1" test -z "$REMOVE_ME" test_stop test_start "special-vars" export DIRENV_BASH=$(command -v bash) export DIRENV_CONFIG=foobar direnv_eval || true test -n "$DIRENV_BASH" test_eq "$DIRENV_CONFIG" "foobar" unset DIRENV_BASH unset DIRENV_CONFIG test_stop test_start "dump" direnv_eval test_eq "$LS_COLORS" "*.ogg=38;5;45:*.wav=38;5;45" test_eq "$THREE_BACKSLASHES" '\\\' test_eq "$LESSOPEN" "||/usr/bin/lesspipe.sh %s" test_stop test_start "empty-var" direnv_eval test_neq "${FOO-unset}" "unset" test_eq "${FOO}" "" test_stop test_start "empty-var-unset" export FOO="" direnv_eval test_eq "${FOO-unset}" "unset" unset FOO test_stop test_start "in-envrc" direnv_eval set +e ./test-in-envrc es=$? set -e test_eq "$es" "1" test_stop test_start "missing-file-source-env" direnv_eval test_stop test_start "symlink-changed" # when using a symlink, reload if the symlink changes, or if the # target file changes. ln -fs ./state-A ./symlink direnv_eval test_eq "${STATE}" "A" sleep 1 ln -fs ./state-B ./symlink direnv_eval test_eq "${STATE}" "B" test_stop # Context: foo/bar is a symlink to ../baz. foo/ contains and .envrc file # BUG: foo/bar is resolved in the .envrc execution context and so can't find # the .envrc file. # # Apparently, the CHDIR syscall does that so I don't know how to work around # the issue. # # test_start "symlink-bug" # cd foo/bar # direnv_eval # test_stop # Pending: test that the mtime is looked on the original file # test_start "utils" # LINK_TIME=`direnv file-mtime link-to-somefile` # touch somefile # NEW_LINK_TIME=`direnv file-mtime link-to-somefile` # test "$LINK_TIME" = "$NEW_LINK_TIME" # test_stop direnv-2.21.2/test/direnv-test.bash000066400000000000000000000001321361411000000171250ustar00rootroot00000000000000#!/usr/bin/env bash export TARGET_SHELL=bash source "$(dirname $0)/direnv-test-common.sh" direnv-2.21.2/test/direnv-test.elv000077500000000000000000000042351361411000000170110ustar00rootroot00000000000000#!/usr/bin/env elvish E:TEST_DIR = (path-dir (src)[path]) set-env XDG_CONFIG_HOME $E:TEST_DIR/config set-env XDG_DATA_HOME $E:TEST_DIR/data E:PATH = (path-dir $E:TEST_DIR):$E:PATH cd $E:TEST_DIR ## reset the direnv loading if any set-env DIRENV_CONFIG $pwd unset-env DIRENV_BASH unset-env DIRENV_DIR unset-env DIRENV_MTIME unset-env DIRENV_WATCHES unset-env DIRENV_DIFF mkdir -p $E:XDG_CONFIG_HOME/direnv touch $E:XDG_CONFIG_HOME/direnv/direnvrc fn direnv-eval { try { m = (direnv export elvish | from-json) keys $m | each [k]{ if (==s $k 'null') { unset-env $k } else { set-env $k $m[$k] } } } except e { nop } } fn test-debug { if (==s $E:DIRENV_DEBUG "1") { echo } } fn test-eq [a b]{ if (!=s $a $b) { fail "FAILED: '"$a"' == '"$b"'" } } fn test-neq [a b]{ if (==s $a $b) { fail "FAILED: '"$a"' != '"$b"'" } } fn test-scenario [name fct]{ cd $E:TEST_DIR/scenarios/$name direnv allow test-debug echo "\n## Testing "$name" ##" test-debug $fct cd $E:TEST_DIR direnv-eval } ### RUN ### try { direnv allow } except e { nop } direnv-eval test-scenario base { echo "Setting up" direnv-eval test-eq $E:HELLO "world" E:WATCHES=$E:DIRENV_WATCHES echo "Reloading (should be no-op)" direnv-eval test-eq $E:WATCHES $E:DIRENV_WATCHES sleep 1 echo "Updating envrc and reloading (should reload)" touch .envrc direnv-eval test-neq $E:WATCHES $E:DIRENV_WATCHES echo "Leaving dir (should clear env set by dir's envrc)" cd .. direnv-eval test-eq $E:HELLO "" } test-scenario inherit { cp ../base/.envrc ../inherited/.envrc direnv-eval echo "HELLO should be world:"$E:HELLO test-eq $E:HELLO "world" sleep 1 echo "export HELLO=goodbye" > ../inherited/.envrc direnv-eval test-eq $E:HELLO "goodbye" } test-scenario "ruby-layout" { direnv-eval test-neq $E:GEM_HOME "" } test-scenario "space dir" { direnv-eval test-eq $E:SPACE_DIR "true" } test-scenario "child-env" { direnv-eval test-eq $E:PARENT_PRE "1" test-eq $E:CHILD "1" test-eq $E:PARENT_POST "1" test-eq $E:REMOVE_ME "" } ## TODO: special-vars ## TODO: dump ## TODO: empty-var ## TODO: empty-var-unset test-scenario "missing-file-source-env" { direnv-eval } direnv-2.21.2/test/direnv-test.fish000077500000000000000000000020511361411000000171460ustar00rootroot00000000000000#!/usr/bin/env fish function eq --argument-names a b if not test (count $argv) = 2 echo "Error: " (count $argv) " arguments passed to `eq`: $argv" exit 1 end if not test $a = $b printf "Error:\n - expected: %s\n - got: %s\n" "$a" "$b" exit 1 end end cd (dirname (status -f)) set TEST_DIR $PWD set XDG_CONFIG_HOME $TEST_DIR/config set XDG_DATA_HOME $TEST_DIR/data set -gx PATH (dirname $TEST_DIR) $PATH # Reset the direnv loading if any set -x DIRENV_CONFIG $PWD set -e DIRENV_BASH set -e DIRENV_DIR set -e DIRENV_WATCHES set -e DIRENV_MTIME set -e DIRENV_DIFF function direnv_eval #direnv export fish # for debugging direnv export fish | source end function test_start -a name cd "$TEST_DIR/scenarios/$name" direnv allow echo "## Testing $name ##" pwd end function test_stop cd $TEST_DIR direnv_eval end ### RUN ### direnv allow direnv_eval test_start dump set -e LS_COLORS direnv_eval eq "$LS_COLORS" "*.ogg=38;5;45:*.wav=38;5;45" eq "$LESSOPEN" "||/usr/bin/lesspipe.sh %s" eq "$THREE_BACKSLASHES" "\\\\\\" test_stop direnv-2.21.2/test/direnv-test.tcsh000077500000000000000000000060241361411000000171620ustar00rootroot00000000000000#!/usr/bin/env tcsh -e cd `dirname $0` setenv TEST_DIR $PWD setenv PATH `dirname $TEST_DIR`:$PATH setenv XDG_CONFIG_HOME $TEST_DIR/config setenv XDG_DATA_HOME $TEST_DIR/data # Reset the direnv loading if any setenv DIRENV_CONFIG $PWD unsetenv DIRENV_BASH unsetenv DIRENV_DIR unsetenv DIRENV_MTIME unsetenv DIRENV_WATCHES unsetenv DIRENV_DIFF alias direnv_eval 'eval `direnv export tcsh`' # test_start() { # cd "$TEST_DIR/scenarios/$1" # direnv allow # echo "## Testing $1 ##" # } # test_stop { # cd $TEST_DIR # direnv_eval # } ### RUN ### direnv allow || true direnv_eval cd $TEST_DIR/scenarios/base echo "Testing base" direnv_eval test "$HELLO" = "world" setenv WATCHES $DIRENV_WATCHES direnv_eval test "$WATCHES" = "$DIRENV_WATCHES" sleep 1 touch .envrc direnv_eval test "$WATCHES" != "$DIRENV_WATCHES" cd .. direnv_eval test 0 -eq "$?HELLO" cd $TEST_DIR ; direnv_eval cd $TEST_DIR/scenarios/inherit cp ../base/.envrc ../inherited/.envrc direnv allow echo "Testing inherit" direnv_eval test "$HELLO" = "world" sleep 1 echo "export HELLO=goodbye" > ../inherited/.envrc direnv_eval test "$HELLO" = "goodbye" cd $TEST_DIR ; direnv_eval cd $TEST_DIR/scenarios/ruby-layout direnv allow echo "Testing ruby-layout" direnv_eval test "$GEM_HOME" != "" cd $TEST_DIR ; direnv_eval # Make sure directories with spaces are fine cd $TEST_DIR/scenarios/"space dir" direnv allow echo "Testing space dir" direnv_eval test "$SPACE_DIR" = "true" cd $TEST_DIR ; direnv_eval cd $TEST_DIR/scenarios/child-env direnv allow echo "Testing child-env" direnv_eval test "$PARENT_PRE" = "1" test "$CHILD" = "1" test "$PARENT_POST" = "1" test 0 -eq "$?REMOVE_ME" cd $TEST_DIR ; direnv_eval # cd $TEST_DIR/scenarios/special-vars # direnv allow # echo "Testing special-vars" # setenv DIRENV_BASH `which bash` # setenv DIRENV_CONFIG foobar # direnv_eval || true # test -n "$DIRENV_BASH" # test "$DIRENV_CONFIG" = "foobar" # unsetenv DIRENV_BASH # unsetenv DIRENV_CONFIG # cd $TEST_DIR ; direnv_eval cd $TEST_DIR/scenarios/"empty-var" direnv allow echo "Testing empty-var" direnv_eval test "$?FOO" -eq 1 test "$FOO" = "" cd $TEST_DIR ; direnv_eval cd $TEST_DIR/scenarios/"empty-var-unset" direnv allow echo "Testing empty-var-unset" setenv FOO "" direnv_eval test "$?FOO" -eq '0' unsetenv FOO cd $TEST_DIR ; direnv_eval # Context: foo/bar is a symlink to ../baz. foo/ contains and .envrc file # BUG: foo/bar is resolved in the .envrc execution context and so can't find # the .envrc file. # # Apparently, the CHDIR syscall does that so I don't know how to work around # the issue. # # cd $TEST_DIR/scenarios/"symlink-bug" # cd foo/bar # direnv_eval # cd $TEST_DIR ; direnv_eval # Pending: test that the mtime is looked on the original file # cd $TEST_DIR/scenarios/"utils" # LINK_TIME=`direnv file-mtime link-to-somefile` # touch somefile # NEW_LINK_TIME=`direnv file-mtime link-to-somefile` # test "$LINK_TIME" = "$NEW_LINK_TIME" # cd $TEST_DIR ; direnv_eval direnv-2.21.2/test/direnv-test.zsh000077500000000000000000000001301361411000000170150ustar00rootroot00000000000000#!/usr/bin/env zsh export TARGET_SHELL=zsh source "$(dirname $0)/direnv-test-common.sh" direnv-2.21.2/test/scenarios/000077500000000000000000000000001361411000000160145ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/base/000077500000000000000000000000001361411000000167265ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/base/.envrc000066400000000000000000000000231361411000000200370ustar00rootroot00000000000000export HELLO=world direnv-2.21.2/test/scenarios/child-env/000077500000000000000000000000001361411000000176655ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/child-env/.envrc000066400000000000000000000002061361411000000210010ustar00rootroot00000000000000set -e export PARENT_PRE=1 export REMOVE_ME=1 direnv_load bash -c 'export CHILD=1; unset REMOVE_ME; direnv dump' export PARENT_POST=1 direnv-2.21.2/test/scenarios/dump/000077500000000000000000000000001361411000000167615ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/dump/.envrc000066400000000000000000000003001361411000000200700ustar00rootroot00000000000000direnv_load env \ LS_COLORS='*.ogg=38;5;45:*.wav=38;5;45' \ LESSOPEN='||/usr/bin/lesspipe.sh %s' \ THREE_BACKSLASHES='\\\' \ bash -c "echo to stdout && echo to stderr >&2 && direnv dump" direnv-2.21.2/test/scenarios/empty-var-unset/000077500000000000000000000000001361411000000210745ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/empty-var-unset/.envrc000066400000000000000000000000111361411000000222020ustar00rootroot00000000000000unset FOOdirenv-2.21.2/test/scenarios/empty-var/000077500000000000000000000000001361411000000177405ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/empty-var/.envrc000066400000000000000000000000151361411000000210520ustar00rootroot00000000000000export FOO=""direnv-2.21.2/test/scenarios/in-envrc/000077500000000000000000000000001361411000000175355ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/in-envrc/test-in-envrc000077500000000000000000000000701361411000000221560ustar00rootroot00000000000000#!/bin/sh test -n "$DIRENV_IN_ENVRC" && echo "in envrc" direnv-2.21.2/test/scenarios/inherit/000077500000000000000000000000001361411000000174565ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/inherit/.envrc000066400000000000000000000000531361411000000205720ustar00rootroot00000000000000source_env ../inherited/.envrc echo $HELLO direnv-2.21.2/test/scenarios/inherited/000077500000000000000000000000001361411000000177675ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/inherited/.gitkeep000066400000000000000000000000001361411000000214060ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/missing-file-source-env/000077500000000000000000000000001361411000000224665ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/missing-file-source-env/.envrc000066400000000000000000000000561361411000000236050ustar00rootroot00000000000000source_env "$(mktemp -d -t $RANDOM.XXXXXXXX)" direnv-2.21.2/test/scenarios/ruby-layout.env000066400000000000000000000001731361411000000210230ustar00rootroot00000000000000export PATH=`pwd`/ruby-layout/bin:$PATH export RUBYLIB=`pwd`/ruby-layout/lib export GEM_HOME=`pwd`/ruby-layout/vendor/gems direnv-2.21.2/test/scenarios/ruby-layout/000077500000000000000000000000001361411000000203105ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/ruby-layout/.envrc000066400000000000000000000000161361411000000214230ustar00rootroot00000000000000layout "ruby" direnv-2.21.2/test/scenarios/space dir/000077500000000000000000000000001361411000000176465ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/space dir/.envrc000066400000000000000000000000451361411000000207630ustar00rootroot00000000000000PATH_add "bin" export SPACE_DIR=true direnv-2.21.2/test/scenarios/special-vars/000077500000000000000000000000001361411000000204055ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/special-vars/.envrc000066400000000000000000000000001361411000000215110ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/symlink-bug/000077500000000000000000000000001361411000000202555ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/symlink-bug/baz/000077500000000000000000000000001361411000000210315ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/symlink-bug/baz/.keep000066400000000000000000000000001361411000000217440ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/symlink-bug/foo/000077500000000000000000000000001361411000000210405ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/symlink-bug/foo/.envrc000066400000000000000000000000001361411000000221440ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/symlink-bug/foo/bar000077700000000000000000000000001361411000000216602..ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/symlink-changed/000077500000000000000000000000001361411000000210715ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/symlink-changed/.envrc000066400000000000000000000000411361411000000222020ustar00rootroot00000000000000watch_file ./symlink . ./symlink direnv-2.21.2/test/scenarios/symlink-changed/.gitignore000066400000000000000000000000101361411000000230500ustar00rootroot00000000000000symlink direnv-2.21.2/test/scenarios/symlink-changed/state-A000066400000000000000000000000171361411000000223100ustar00rootroot00000000000000export STATE=A direnv-2.21.2/test/scenarios/symlink-changed/state-B000066400000000000000000000000171361411000000223110ustar00rootroot00000000000000export STATE=B direnv-2.21.2/test/scenarios/use-rvm/000077500000000000000000000000001361411000000174125ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/use-rvm/.envrc000066400000000000000000000000161361411000000205250ustar00rootroot00000000000000rvm use 1.8.7 direnv-2.21.2/test/scenarios/utils/000077500000000000000000000000001361411000000171545ustar00rootroot00000000000000direnv-2.21.2/test/scenarios/utils/link-to-file000077700000000000000000000000001361411000000231122somefileustar00rootroot00000000000000direnv-2.21.2/test/scenarios/utils/somefile000066400000000000000000000000001361411000000206700ustar00rootroot00000000000000direnv-2.21.2/test/show-direnv-diff.sh000077500000000000000000000003341361411000000175400ustar00rootroot00000000000000#!/bin/sh # # Small util to display the content of the current DIRENV_DIFF env var. # GZIP_HEADER="\x1f\x8b\x08\x00\x00\x00\x00\x00" (printf $GZIP_HEADER; echo $DIRENV_DIFF | base64 -D) | gzip -dc | python -mjson.tool direnv-2.21.2/test/stdlib.bash000077500000000000000000000010231361411000000161450ustar00rootroot00000000000000#!/usr/bin/env bash set -euo pipefail # always execute relative to here cd "$(dirname "$0")" # add the built direnv to the path root=$(cd .. && pwd -P) export PATH=$root:$PATH load_stdlib() { # shellcheck disable=SC1090 source "$root/stdlib.sh" } assert_eq() { if [[ $1 != "$2" ]]; then echo "expected '$1' to equal '$2'" return 1 fi } # test find_up ( load_stdlib path=$(find_up "README.md") assert_eq "$path" "$root/README.md" ) # test source_up ( load_stdlib cd scenarios/inherited source_up ) direnv-2.21.2/vendor/000077500000000000000000000000001361411000000143445ustar00rootroot00000000000000direnv-2.21.2/vendor/github.com/000077500000000000000000000000001361411000000164035ustar00rootroot00000000000000direnv-2.21.2/vendor/github.com/BurntSushi/000077500000000000000000000000001361411000000205115ustar00rootroot00000000000000direnv-2.21.2/vendor/github.com/BurntSushi/toml/000077500000000000000000000000001361411000000214645ustar00rootroot00000000000000direnv-2.21.2/vendor/github.com/BurntSushi/toml/.gitignore000066400000000000000000000000571361411000000234560ustar00rootroot00000000000000TAGS tags .*.swp tomlcheck/tomlcheck toml.test direnv-2.21.2/vendor/github.com/BurntSushi/toml/.travis.yml000066400000000000000000000003211361411000000235710ustar00rootroot00000000000000language: go go: - 1.1 - 1.2 - 1.3 - 1.4 - 1.5 - 1.6 - tip install: - go install ./... - go get github.com/BurntSushi/toml-test script: - export PATH="$PATH:$HOME/gopath/bin" - make test direnv-2.21.2/vendor/github.com/BurntSushi/toml/COMPATIBLE000066400000000000000000000001611361411000000230240ustar00rootroot00000000000000Compatible with TOML version [v0.4.0](https://github.com/toml-lang/toml/blob/v0.4.0/versions/en/toml-v0.4.0.md) direnv-2.21.2/vendor/github.com/BurntSushi/toml/COPYING000066400000000000000000000020671361411000000225240ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013 TOML authors 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. direnv-2.21.2/vendor/github.com/BurntSushi/toml/Makefile000066400000000000000000000004411361411000000231230ustar00rootroot00000000000000install: go install ./... test: install go test -v toml-test toml-test-decoder toml-test -encoder toml-test-encoder fmt: gofmt -w *.go */*.go colcheck *.go */*.go tags: find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS push: git push origin master git push github master direnv-2.21.2/vendor/github.com/BurntSushi/toml/README.md000066400000000000000000000103571361411000000227510ustar00rootroot00000000000000## TOML parser and encoder for Go with reflection TOML stands for Tom's Obvious, Minimal Language. This Go package provides a reflection interface similar to Go's standard library `json` and `xml` packages. This package also supports the `encoding.TextUnmarshaler` and `encoding.TextMarshaler` interfaces so that you can define custom data representations. (There is an example of this below.) Spec: https://github.com/toml-lang/toml Compatible with TOML version [v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md) Documentation: https://godoc.org/github.com/BurntSushi/toml Installation: ```bash go get github.com/BurntSushi/toml ``` Try the toml validator: ```bash go get github.com/BurntSushi/toml/cmd/tomlv tomlv some-toml-file.toml ``` [![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](https://godoc.org/github.com/BurntSushi/toml) ### Testing This package passes all tests in [toml-test](https://github.com/BurntSushi/toml-test) for both the decoder and the encoder. ### Examples This package works similarly to how the Go standard library handles `XML` and `JSON`. Namely, data is loaded into Go values via reflection. For the simplest example, consider some TOML file as just a list of keys and values: ```toml Age = 25 Cats = [ "Cauchy", "Plato" ] Pi = 3.14 Perfection = [ 6, 28, 496, 8128 ] DOB = 1987-07-05T05:45:00Z ``` Which could be defined in Go as: ```go type Config struct { Age int Cats []string Pi float64 Perfection []int DOB time.Time // requires `import time` } ``` And then decoded with: ```go var conf Config if _, err := toml.Decode(tomlData, &conf); err != nil { // handle error } ``` You can also use struct tags if your struct field name doesn't map to a TOML key value directly: ```toml some_key_NAME = "wat" ``` ```go type TOML struct { ObscureKey string `toml:"some_key_NAME"` } ``` ### Using the `encoding.TextUnmarshaler` interface Here's an example that automatically parses duration strings into `time.Duration` values: ```toml [[song]] name = "Thunder Road" duration = "4m49s" [[song]] name = "Stairway to Heaven" duration = "8m03s" ``` Which can be decoded with: ```go type song struct { Name string Duration duration } type songs struct { Song []song } var favorites songs if _, err := toml.Decode(blob, &favorites); err != nil { log.Fatal(err) } for _, s := range favorites.Song { fmt.Printf("%s (%s)\n", s.Name, s.Duration) } ``` And you'll also need a `duration` type that satisfies the `encoding.TextUnmarshaler` interface: ```go type duration struct { time.Duration } func (d *duration) UnmarshalText(text []byte) error { var err error d.Duration, err = time.ParseDuration(string(text)) return err } ``` ### More complex usage Here's an example of how to load the example from the official spec page: ```toml # This is a TOML document. Boom. title = "TOML Example" [owner] name = "Tom Preston-Werner" organization = "GitHub" bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." dob = 1979-05-27T07:32:00Z # First class dates? Why not? [database] server = "192.168.1.1" ports = [ 8001, 8001, 8002 ] connection_max = 5000 enabled = true [servers] # You can indent as you please. Tabs or spaces. TOML don't care. [servers.alpha] ip = "10.0.0.1" dc = "eqdc10" [servers.beta] ip = "10.0.0.2" dc = "eqdc10" [clients] data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it # Line breaks are OK when inside arrays hosts = [ "alpha", "omega" ] ``` And the corresponding Go types are: ```go type tomlConfig struct { Title string Owner ownerInfo DB database `toml:"database"` Servers map[string]server Clients clients } type ownerInfo struct { Name string Org string `toml:"organization"` Bio string DOB time.Time } type database struct { Server string Ports []int ConnMax int `toml:"connection_max"` Enabled bool } type server struct { IP string DC string } type clients struct { Data [][]interface{} Hosts []string } ``` Note that a case insensitive match will be tried if an exact match can't be found. A working example of the above can be found in `_examples/example.{go,toml}`. direnv-2.21.2/vendor/github.com/BurntSushi/toml/decode.go000066400000000000000000000341141361411000000232410ustar00rootroot00000000000000package toml import ( "fmt" "io" "io/ioutil" "math" "reflect" "strings" "time" ) func e(format string, args ...interface{}) error { return fmt.Errorf("toml: "+format, args...) } // Unmarshaler is the interface implemented by objects that can unmarshal a // TOML description of themselves. type Unmarshaler interface { UnmarshalTOML(interface{}) error } // Unmarshal decodes the contents of `p` in TOML format into a pointer `v`. func Unmarshal(p []byte, v interface{}) error { _, err := Decode(string(p), v) return err } // Primitive is a TOML value that hasn't been decoded into a Go value. // When using the various `Decode*` functions, the type `Primitive` may // be given to any value, and its decoding will be delayed. // // A `Primitive` value can be decoded using the `PrimitiveDecode` function. // // The underlying representation of a `Primitive` value is subject to change. // Do not rely on it. // // N.B. Primitive values are still parsed, so using them will only avoid // the overhead of reflection. They can be useful when you don't know the // exact type of TOML data until run time. type Primitive struct { undecoded interface{} context Key } // DEPRECATED! // // Use MetaData.PrimitiveDecode instead. func PrimitiveDecode(primValue Primitive, v interface{}) error { md := MetaData{decoded: make(map[string]bool)} return md.unify(primValue.undecoded, rvalue(v)) } // PrimitiveDecode is just like the other `Decode*` functions, except it // decodes a TOML value that has already been parsed. Valid primitive values // can *only* be obtained from values filled by the decoder functions, // including this method. (i.e., `v` may contain more `Primitive` // values.) // // Meta data for primitive values is included in the meta data returned by // the `Decode*` functions with one exception: keys returned by the Undecoded // method will only reflect keys that were decoded. Namely, any keys hidden // behind a Primitive will be considered undecoded. Executing this method will // update the undecoded keys in the meta data. (See the example.) func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { md.context = primValue.context defer func() { md.context = nil }() return md.unify(primValue.undecoded, rvalue(v)) } // Decode will decode the contents of `data` in TOML format into a pointer // `v`. // // TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be // used interchangeably.) // // TOML arrays of tables correspond to either a slice of structs or a slice // of maps. // // TOML datetimes correspond to Go `time.Time` values. // // All other TOML types (float, string, int, bool and array) correspond // to the obvious Go types. // // An exception to the above rules is if a type implements the // encoding.TextUnmarshaler interface. In this case, any primitive TOML value // (floats, strings, integers, booleans and datetimes) will be converted to // a byte string and given to the value's UnmarshalText method. See the // Unmarshaler example for a demonstration with time duration strings. // // Key mapping // // TOML keys can map to either keys in a Go map or field names in a Go // struct. The special `toml` struct tag may be used to map TOML keys to // struct fields that don't match the key name exactly. (See the example.) // A case insensitive match to struct names will be tried if an exact match // can't be found. // // The mapping between TOML values and Go values is loose. That is, there // may exist TOML values that cannot be placed into your representation, and // there may be parts of your representation that do not correspond to // TOML values. This loose mapping can be made stricter by using the IsDefined // and/or Undecoded methods on the MetaData returned. // // This decoder will not handle cyclic types. If a cyclic type is passed, // `Decode` will not terminate. func Decode(data string, v interface{}) (MetaData, error) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr { return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v)) } if rv.IsNil() { return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v)) } p, err := parse(data) if err != nil { return MetaData{}, err } md := MetaData{ p.mapping, p.types, p.ordered, make(map[string]bool, len(p.ordered)), nil, } return md, md.unify(p.mapping, indirect(rv)) } // DecodeFile is just like Decode, except it will automatically read the // contents of the file at `fpath` and decode it for you. func DecodeFile(fpath string, v interface{}) (MetaData, error) { bs, err := ioutil.ReadFile(fpath) if err != nil { return MetaData{}, err } return Decode(string(bs), v) } // DecodeReader is just like Decode, except it will consume all bytes // from the reader and decode it for you. func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { bs, err := ioutil.ReadAll(r) if err != nil { return MetaData{}, err } return Decode(string(bs), v) } // unify performs a sort of type unification based on the structure of `rv`, // which is the client representation. // // Any type mismatch produces an error. Finding a type that we don't know // how to handle produces an unsupported type error. func (md *MetaData) unify(data interface{}, rv reflect.Value) error { // Special case. Look for a `Primitive` value. if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() { // Save the undecoded data and the key context into the primitive // value. context := make(Key, len(md.context)) copy(context, md.context) rv.Set(reflect.ValueOf(Primitive{ undecoded: data, context: context, })) return nil } // Special case. Unmarshaler Interface support. if rv.CanAddr() { if v, ok := rv.Addr().Interface().(Unmarshaler); ok { return v.UnmarshalTOML(data) } } // Special case. Handle time.Time values specifically. // TODO: Remove this code when we decide to drop support for Go 1.1. // This isn't necessary in Go 1.2 because time.Time satisfies the encoding // interfaces. if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) { return md.unifyDatetime(data, rv) } // Special case. Look for a value satisfying the TextUnmarshaler interface. if v, ok := rv.Interface().(TextUnmarshaler); ok { return md.unifyText(data, v) } // BUG(burntsushi) // The behavior here is incorrect whenever a Go type satisfies the // encoding.TextUnmarshaler interface but also corresponds to a TOML // hash or array. In particular, the unmarshaler should only be applied // to primitive TOML values. But at this point, it will be applied to // all kinds of values and produce an incorrect error whenever those values // are hashes or arrays (including arrays of tables). k := rv.Kind() // laziness if k >= reflect.Int && k <= reflect.Uint64 { return md.unifyInt(data, rv) } switch k { case reflect.Ptr: elem := reflect.New(rv.Type().Elem()) err := md.unify(data, reflect.Indirect(elem)) if err != nil { return err } rv.Set(elem) return nil case reflect.Struct: return md.unifyStruct(data, rv) case reflect.Map: return md.unifyMap(data, rv) case reflect.Array: return md.unifyArray(data, rv) case reflect.Slice: return md.unifySlice(data, rv) case reflect.String: return md.unifyString(data, rv) case reflect.Bool: return md.unifyBool(data, rv) case reflect.Interface: // we only support empty interfaces. if rv.NumMethod() > 0 { return e("unsupported type %s", rv.Type()) } return md.unifyAnything(data, rv) case reflect.Float32: fallthrough case reflect.Float64: return md.unifyFloat64(data, rv) } return e("unsupported type %s", rv.Kind()) } func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { tmap, ok := mapping.(map[string]interface{}) if !ok { if mapping == nil { return nil } return e("type mismatch for %s: expected table but found %T", rv.Type().String(), mapping) } for key, datum := range tmap { var f *field fields := cachedTypeFields(rv.Type()) for i := range fields { ff := &fields[i] if ff.name == key { f = ff break } if f == nil && strings.EqualFold(ff.name, key) { f = ff } } if f != nil { subv := rv for _, i := range f.index { subv = indirect(subv.Field(i)) } if isUnifiable(subv) { md.decoded[md.context.add(key).String()] = true md.context = append(md.context, key) if err := md.unify(datum, subv); err != nil { return err } md.context = md.context[0 : len(md.context)-1] } else if f.name != "" { // Bad user! No soup for you! return e("cannot write unexported field %s.%s", rv.Type().String(), f.name) } } } return nil } func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { tmap, ok := mapping.(map[string]interface{}) if !ok { if tmap == nil { return nil } return badtype("map", mapping) } if rv.IsNil() { rv.Set(reflect.MakeMap(rv.Type())) } for k, v := range tmap { md.decoded[md.context.add(k).String()] = true md.context = append(md.context, k) rvkey := indirect(reflect.New(rv.Type().Key())) rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) if err := md.unify(v, rvval); err != nil { return err } md.context = md.context[0 : len(md.context)-1] rvkey.SetString(k) rv.SetMapIndex(rvkey, rvval) } return nil } func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { datav := reflect.ValueOf(data) if datav.Kind() != reflect.Slice { if !datav.IsValid() { return nil } return badtype("slice", data) } sliceLen := datav.Len() if sliceLen != rv.Len() { return e("expected array length %d; got TOML array of length %d", rv.Len(), sliceLen) } return md.unifySliceArray(datav, rv) } func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { datav := reflect.ValueOf(data) if datav.Kind() != reflect.Slice { if !datav.IsValid() { return nil } return badtype("slice", data) } n := datav.Len() if rv.IsNil() || rv.Cap() < n { rv.Set(reflect.MakeSlice(rv.Type(), n, n)) } rv.SetLen(n) return md.unifySliceArray(datav, rv) } func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { sliceLen := data.Len() for i := 0; i < sliceLen; i++ { v := data.Index(i).Interface() sliceval := indirect(rv.Index(i)) if err := md.unify(v, sliceval); err != nil { return err } } return nil } func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error { if _, ok := data.(time.Time); ok { rv.Set(reflect.ValueOf(data)) return nil } return badtype("time.Time", data) } func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { if s, ok := data.(string); ok { rv.SetString(s) return nil } return badtype("string", data) } func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { if num, ok := data.(float64); ok { switch rv.Kind() { case reflect.Float32: fallthrough case reflect.Float64: rv.SetFloat(num) default: panic("bug") } return nil } return badtype("float", data) } func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { if num, ok := data.(int64); ok { if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 { switch rv.Kind() { case reflect.Int, reflect.Int64: // No bounds checking necessary. case reflect.Int8: if num < math.MinInt8 || num > math.MaxInt8 { return e("value %d is out of range for int8", num) } case reflect.Int16: if num < math.MinInt16 || num > math.MaxInt16 { return e("value %d is out of range for int16", num) } case reflect.Int32: if num < math.MinInt32 || num > math.MaxInt32 { return e("value %d is out of range for int32", num) } } rv.SetInt(num) } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 { unum := uint64(num) switch rv.Kind() { case reflect.Uint, reflect.Uint64: // No bounds checking necessary. case reflect.Uint8: if num < 0 || unum > math.MaxUint8 { return e("value %d is out of range for uint8", num) } case reflect.Uint16: if num < 0 || unum > math.MaxUint16 { return e("value %d is out of range for uint16", num) } case reflect.Uint32: if num < 0 || unum > math.MaxUint32 { return e("value %d is out of range for uint32", num) } } rv.SetUint(unum) } else { panic("unreachable") } return nil } return badtype("integer", data) } func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { if b, ok := data.(bool); ok { rv.SetBool(b) return nil } return badtype("boolean", data) } func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { rv.Set(reflect.ValueOf(data)) return nil } func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error { var s string switch sdata := data.(type) { case TextMarshaler: text, err := sdata.MarshalText() if err != nil { return err } s = string(text) case fmt.Stringer: s = sdata.String() case string: s = sdata case bool: s = fmt.Sprintf("%v", sdata) case int64: s = fmt.Sprintf("%d", sdata) case float64: s = fmt.Sprintf("%f", sdata) default: return badtype("primitive (string-like)", data) } if err := v.UnmarshalText([]byte(s)); err != nil { return err } return nil } // rvalue returns a reflect.Value of `v`. All pointers are resolved. func rvalue(v interface{}) reflect.Value { return indirect(reflect.ValueOf(v)) } // indirect returns the value pointed to by a pointer. // Pointers are followed until the value is not a pointer. // New values are allocated for each nil pointer. // // An exception to this rule is if the value satisfies an interface of // interest to us (like encoding.TextUnmarshaler). func indirect(v reflect.Value) reflect.Value { if v.Kind() != reflect.Ptr { if v.CanSet() { pv := v.Addr() if _, ok := pv.Interface().(TextUnmarshaler); ok { return pv } } return v } if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } return indirect(reflect.Indirect(v)) } func isUnifiable(rv reflect.Value) bool { if rv.CanSet() { return true } if _, ok := rv.Interface().(TextUnmarshaler); ok { return true } return false } func badtype(expected string, data interface{}) error { return e("cannot load TOML value of type %T into a Go %s", data, expected) } direnv-2.21.2/vendor/github.com/BurntSushi/toml/decode_meta.go000066400000000000000000000062021361411000000242440ustar00rootroot00000000000000package toml import "strings" // MetaData allows access to meta information about TOML data that may not // be inferrable via reflection. In particular, whether a key has been defined // and the TOML type of a key. type MetaData struct { mapping map[string]interface{} types map[string]tomlType keys []Key decoded map[string]bool context Key // Used only during decoding. } // IsDefined returns true if the key given exists in the TOML data. The key // should be specified hierarchially. e.g., // // // access the TOML key 'a.b.c' // IsDefined("a", "b", "c") // // IsDefined will return false if an empty key given. Keys are case sensitive. func (md *MetaData) IsDefined(key ...string) bool { if len(key) == 0 { return false } var hash map[string]interface{} var ok bool var hashOrVal interface{} = md.mapping for _, k := range key { if hash, ok = hashOrVal.(map[string]interface{}); !ok { return false } if hashOrVal, ok = hash[k]; !ok { return false } } return true } // Type returns a string representation of the type of the key specified. // // Type will return the empty string if given an empty key or a key that // does not exist. Keys are case sensitive. func (md *MetaData) Type(key ...string) string { fullkey := strings.Join(key, ".") if typ, ok := md.types[fullkey]; ok { return typ.typeString() } return "" } // Key is the type of any TOML key, including key groups. Use (MetaData).Keys // to get values of this type. type Key []string func (k Key) String() string { return strings.Join(k, ".") } func (k Key) maybeQuotedAll() string { var ss []string for i := range k { ss = append(ss, k.maybeQuoted(i)) } return strings.Join(ss, ".") } func (k Key) maybeQuoted(i int) string { quote := false for _, c := range k[i] { if !isBareKeyChar(c) { quote = true break } } if quote { return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\"" } return k[i] } func (k Key) add(piece string) Key { newKey := make(Key, len(k)+1) copy(newKey, k) newKey[len(k)] = piece return newKey } // Keys returns a slice of every key in the TOML data, including key groups. // Each key is itself a slice, where the first element is the top of the // hierarchy and the last is the most specific. // // The list will have the same order as the keys appeared in the TOML data. // // All keys returned are non-empty. func (md *MetaData) Keys() []Key { return md.keys } // Undecoded returns all keys that have not been decoded in the order in which // they appear in the original TOML document. // // This includes keys that haven't been decoded because of a Primitive value. // Once the Primitive value is decoded, the keys will be considered decoded. // // Also note that decoding into an empty interface will result in no decoding, // and so no keys will be considered decoded. // // In this sense, the Undecoded keys correspond to keys in the TOML document // that do not have a concrete type in your representation. func (md *MetaData) Undecoded() []Key { undecoded := make([]Key, 0, len(md.keys)) for _, key := range md.keys { if !md.decoded[key.String()] { undecoded = append(undecoded, key) } } return undecoded } direnv-2.21.2/vendor/github.com/BurntSushi/toml/doc.go000066400000000000000000000021201361411000000225530ustar00rootroot00000000000000/* Package toml provides facilities for decoding and encoding TOML configuration files via reflection. There is also support for delaying decoding with the Primitive type, and querying the set of keys in a TOML document with the MetaData type. The specification implemented: https://github.com/toml-lang/toml The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify whether a file is a valid TOML document. It can also be used to print the type of each key in a TOML document. Testing There are two important types of tests used for this package. The first is contained inside '*_test.go' files and uses the standard Go unit testing framework. These tests are primarily devoted to holistically testing the decoder and encoder. The second type of testing is used to verify the implementation's adherence to the TOML specification. These tests have been factored into their own project: https://github.com/BurntSushi/toml-test The reason the tests are in a separate project is so that they can be used by any implementation of TOML. Namely, it is language agnostic. */ package toml direnv-2.21.2/vendor/github.com/BurntSushi/toml/encode.go000066400000000000000000000351731361411000000232610ustar00rootroot00000000000000package toml import ( "bufio" "errors" "fmt" "io" "reflect" "sort" "strconv" "strings" "time" ) type tomlEncodeError struct{ error } var ( errArrayMixedElementTypes = errors.New( "toml: cannot encode array with mixed element types") errArrayNilElement = errors.New( "toml: cannot encode array with nil element") errNonString = errors.New( "toml: cannot encode a map with non-string key type") errAnonNonStruct = errors.New( "toml: cannot encode an anonymous field that is not a struct") errArrayNoTable = errors.New( "toml: TOML array element cannot contain a table") errNoKey = errors.New( "toml: top-level values must be Go maps or structs") errAnything = errors.New("") // used in testing ) var quotedReplacer = strings.NewReplacer( "\t", "\\t", "\n", "\\n", "\r", "\\r", "\"", "\\\"", "\\", "\\\\", ) // Encoder controls the encoding of Go values to a TOML document to some // io.Writer. // // The indentation level can be controlled with the Indent field. type Encoder struct { // A single indentation level. By default it is two spaces. Indent string // hasWritten is whether we have written any output to w yet. hasWritten bool w *bufio.Writer } // NewEncoder returns a TOML encoder that encodes Go values to the io.Writer // given. By default, a single indentation level is 2 spaces. func NewEncoder(w io.Writer) *Encoder { return &Encoder{ w: bufio.NewWriter(w), Indent: " ", } } // Encode writes a TOML representation of the Go value to the underlying // io.Writer. If the value given cannot be encoded to a valid TOML document, // then an error is returned. // // The mapping between Go values and TOML values should be precisely the same // as for the Decode* functions. Similarly, the TextMarshaler interface is // supported by encoding the resulting bytes as strings. (If you want to write // arbitrary binary data then you will need to use something like base64 since // TOML does not have any binary types.) // // When encoding TOML hashes (i.e., Go maps or structs), keys without any // sub-hashes are encoded first. // // If a Go map is encoded, then its keys are sorted alphabetically for // deterministic output. More control over this behavior may be provided if // there is demand for it. // // Encoding Go values without a corresponding TOML representation---like map // types with non-string keys---will cause an error to be returned. Similarly // for mixed arrays/slices, arrays/slices with nil elements, embedded // non-struct types and nested slices containing maps or structs. // (e.g., [][]map[string]string is not allowed but []map[string]string is OK // and so is []map[string][]string.) func (enc *Encoder) Encode(v interface{}) error { rv := eindirect(reflect.ValueOf(v)) if err := enc.safeEncode(Key([]string{}), rv); err != nil { return err } return enc.w.Flush() } func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) { defer func() { if r := recover(); r != nil { if terr, ok := r.(tomlEncodeError); ok { err = terr.error return } panic(r) } }() enc.encode(key, rv) return nil } func (enc *Encoder) encode(key Key, rv reflect.Value) { // Special case. Time needs to be in ISO8601 format. // Special case. If we can marshal the type to text, then we used that. // Basically, this prevents the encoder for handling these types as // generic structs (or whatever the underlying type of a TextMarshaler is). switch rv.Interface().(type) { case time.Time, TextMarshaler: enc.keyEqElement(key, rv) return } k := rv.Kind() switch k { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String, reflect.Bool: enc.keyEqElement(key, rv) case reflect.Array, reflect.Slice: if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) { enc.eArrayOfTables(key, rv) } else { enc.keyEqElement(key, rv) } case reflect.Interface: if rv.IsNil() { return } enc.encode(key, rv.Elem()) case reflect.Map: if rv.IsNil() { return } enc.eTable(key, rv) case reflect.Ptr: if rv.IsNil() { return } enc.encode(key, rv.Elem()) case reflect.Struct: enc.eTable(key, rv) default: panic(e("unsupported type for key '%s': %s", key, k)) } } // eElement encodes any value that can be an array element (primitives and // arrays). func (enc *Encoder) eElement(rv reflect.Value) { switch v := rv.Interface().(type) { case time.Time: // Special case time.Time as a primitive. Has to come before // TextMarshaler below because time.Time implements // encoding.TextMarshaler, but we need to always use UTC. enc.wf(v.UTC().Format("2006-01-02T15:04:05Z")) return case TextMarshaler: // Special case. Use text marshaler if it's available for this value. if s, err := v.MarshalText(); err != nil { encPanic(err) } else { enc.writeQuoted(string(s)) } return } switch rv.Kind() { case reflect.Bool: enc.wf(strconv.FormatBool(rv.Bool())) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: enc.wf(strconv.FormatInt(rv.Int(), 10)) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: enc.wf(strconv.FormatUint(rv.Uint(), 10)) case reflect.Float32: enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32))) case reflect.Float64: enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64))) case reflect.Array, reflect.Slice: enc.eArrayOrSliceElement(rv) case reflect.Interface: enc.eElement(rv.Elem()) case reflect.String: enc.writeQuoted(rv.String()) default: panic(e("unexpected primitive type: %s", rv.Kind())) } } // By the TOML spec, all floats must have a decimal with at least one // number on either side. func floatAddDecimal(fstr string) string { if !strings.Contains(fstr, ".") { return fstr + ".0" } return fstr } func (enc *Encoder) writeQuoted(s string) { enc.wf("\"%s\"", quotedReplacer.Replace(s)) } func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { length := rv.Len() enc.wf("[") for i := 0; i < length; i++ { elem := rv.Index(i) enc.eElement(elem) if i != length-1 { enc.wf(", ") } } enc.wf("]") } func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { if len(key) == 0 { encPanic(errNoKey) } for i := 0; i < rv.Len(); i++ { trv := rv.Index(i) if isNil(trv) { continue } panicIfInvalidKey(key) enc.newline() enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll()) enc.newline() enc.eMapOrStruct(key, trv) } } func (enc *Encoder) eTable(key Key, rv reflect.Value) { panicIfInvalidKey(key) if len(key) == 1 { // Output an extra newline between top-level tables. // (The newline isn't written if nothing else has been written though.) enc.newline() } if len(key) > 0 { enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll()) enc.newline() } enc.eMapOrStruct(key, rv) } func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) { switch rv := eindirect(rv); rv.Kind() { case reflect.Map: enc.eMap(key, rv) case reflect.Struct: enc.eStruct(key, rv) default: panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String()) } } func (enc *Encoder) eMap(key Key, rv reflect.Value) { rt := rv.Type() if rt.Key().Kind() != reflect.String { encPanic(errNonString) } // Sort keys so that we have deterministic output. And write keys directly // underneath this key first, before writing sub-structs or sub-maps. var mapKeysDirect, mapKeysSub []string for _, mapKey := range rv.MapKeys() { k := mapKey.String() if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) { mapKeysSub = append(mapKeysSub, k) } else { mapKeysDirect = append(mapKeysDirect, k) } } var writeMapKeys = func(mapKeys []string) { sort.Strings(mapKeys) for _, mapKey := range mapKeys { mrv := rv.MapIndex(reflect.ValueOf(mapKey)) if isNil(mrv) { // Don't write anything for nil fields. continue } enc.encode(key.add(mapKey), mrv) } } writeMapKeys(mapKeysDirect) writeMapKeys(mapKeysSub) } func (enc *Encoder) eStruct(key Key, rv reflect.Value) { // Write keys for fields directly under this key first, because if we write // a field that creates a new table, then all keys under it will be in that // table (not the one we're writing here). rt := rv.Type() var fieldsDirect, fieldsSub [][]int var addFields func(rt reflect.Type, rv reflect.Value, start []int) addFields = func(rt reflect.Type, rv reflect.Value, start []int) { for i := 0; i < rt.NumField(); i++ { f := rt.Field(i) // skip unexported fields if f.PkgPath != "" && !f.Anonymous { continue } frv := rv.Field(i) if f.Anonymous { t := f.Type switch t.Kind() { case reflect.Struct: // Treat anonymous struct fields with // tag names as though they are not // anonymous, like encoding/json does. if getOptions(f.Tag).name == "" { addFields(t, frv, f.Index) continue } case reflect.Ptr: if t.Elem().Kind() == reflect.Struct && getOptions(f.Tag).name == "" { if !frv.IsNil() { addFields(t.Elem(), frv.Elem(), f.Index) } continue } // Fall through to the normal field encoding logic below // for non-struct anonymous fields. } } if typeIsHash(tomlTypeOfGo(frv)) { fieldsSub = append(fieldsSub, append(start, f.Index...)) } else { fieldsDirect = append(fieldsDirect, append(start, f.Index...)) } } } addFields(rt, rv, nil) var writeFields = func(fields [][]int) { for _, fieldIndex := range fields { sft := rt.FieldByIndex(fieldIndex) sf := rv.FieldByIndex(fieldIndex) if isNil(sf) { // Don't write anything for nil fields. continue } opts := getOptions(sft.Tag) if opts.skip { continue } keyName := sft.Name if opts.name != "" { keyName = opts.name } if opts.omitempty && isEmpty(sf) { continue } if opts.omitzero && isZero(sf) { continue } enc.encode(key.add(keyName), sf) } } writeFields(fieldsDirect) writeFields(fieldsSub) } // tomlTypeName returns the TOML type name of the Go value's type. It is // used to determine whether the types of array elements are mixed (which is // forbidden). If the Go value is nil, then it is illegal for it to be an array // element, and valueIsNil is returned as true. // Returns the TOML type of a Go value. The type may be `nil`, which means // no concrete TOML type could be found. func tomlTypeOfGo(rv reflect.Value) tomlType { if isNil(rv) || !rv.IsValid() { return nil } switch rv.Kind() { case reflect.Bool: return tomlBool case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return tomlInteger case reflect.Float32, reflect.Float64: return tomlFloat case reflect.Array, reflect.Slice: if typeEqual(tomlHash, tomlArrayType(rv)) { return tomlArrayHash } return tomlArray case reflect.Ptr, reflect.Interface: return tomlTypeOfGo(rv.Elem()) case reflect.String: return tomlString case reflect.Map: return tomlHash case reflect.Struct: switch rv.Interface().(type) { case time.Time: return tomlDatetime case TextMarshaler: return tomlString default: return tomlHash } default: panic("unexpected reflect.Kind: " + rv.Kind().String()) } } // tomlArrayType returns the element type of a TOML array. The type returned // may be nil if it cannot be determined (e.g., a nil slice or a zero length // slize). This function may also panic if it finds a type that cannot be // expressed in TOML (such as nil elements, heterogeneous arrays or directly // nested arrays of tables). func tomlArrayType(rv reflect.Value) tomlType { if isNil(rv) || !rv.IsValid() || rv.Len() == 0 { return nil } firstType := tomlTypeOfGo(rv.Index(0)) if firstType == nil { encPanic(errArrayNilElement) } rvlen := rv.Len() for i := 1; i < rvlen; i++ { elem := rv.Index(i) switch elemType := tomlTypeOfGo(elem); { case elemType == nil: encPanic(errArrayNilElement) case !typeEqual(firstType, elemType): encPanic(errArrayMixedElementTypes) } } // If we have a nested array, then we must make sure that the nested // array contains ONLY primitives. // This checks arbitrarily nested arrays. if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) { nest := tomlArrayType(eindirect(rv.Index(0))) if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) { encPanic(errArrayNoTable) } } return firstType } type tagOptions struct { skip bool // "-" name string omitempty bool omitzero bool } func getOptions(tag reflect.StructTag) tagOptions { t := tag.Get("toml") if t == "-" { return tagOptions{skip: true} } var opts tagOptions parts := strings.Split(t, ",") opts.name = parts[0] for _, s := range parts[1:] { switch s { case "omitempty": opts.omitempty = true case "omitzero": opts.omitzero = true } } return opts } func isZero(rv reflect.Value) bool { switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return rv.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return rv.Uint() == 0 case reflect.Float32, reflect.Float64: return rv.Float() == 0.0 } return false } func isEmpty(rv reflect.Value) bool { switch rv.Kind() { case reflect.Array, reflect.Slice, reflect.Map, reflect.String: return rv.Len() == 0 case reflect.Bool: return !rv.Bool() } return false } func (enc *Encoder) newline() { if enc.hasWritten { enc.wf("\n") } } func (enc *Encoder) keyEqElement(key Key, val reflect.Value) { if len(key) == 0 { encPanic(errNoKey) } panicIfInvalidKey(key) enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) enc.eElement(val) enc.newline() } func (enc *Encoder) wf(format string, v ...interface{}) { if _, err := fmt.Fprintf(enc.w, format, v...); err != nil { encPanic(err) } enc.hasWritten = true } func (enc *Encoder) indentStr(key Key) string { return strings.Repeat(enc.Indent, len(key)-1) } func encPanic(err error) { panic(tomlEncodeError{err}) } func eindirect(v reflect.Value) reflect.Value { switch v.Kind() { case reflect.Ptr, reflect.Interface: return eindirect(v.Elem()) default: return v } } func isNil(rv reflect.Value) bool { switch rv.Kind() { case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return rv.IsNil() default: return false } } func panicIfInvalidKey(key Key) { for _, k := range key { if len(k) == 0 { encPanic(e("Key '%s' is not a valid table name. Key names "+ "cannot be empty.", key.maybeQuotedAll())) } } } func isValidKeyName(s string) bool { return len(s) != 0 } direnv-2.21.2/vendor/github.com/BurntSushi/toml/encoding_types.go000066400000000000000000000010351361411000000250240ustar00rootroot00000000000000// +build go1.2 package toml // In order to support Go 1.1, we define our own TextMarshaler and // TextUnmarshaler types. For Go 1.2+, we just alias them with the // standard library interfaces. import ( "encoding" ) // TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here // so that Go 1.1 can be supported. type TextMarshaler encoding.TextMarshaler // TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined // here so that Go 1.1 can be supported. type TextUnmarshaler encoding.TextUnmarshaler direnv-2.21.2/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go000066400000000000000000000007731361411000000254130ustar00rootroot00000000000000// +build !go1.2 package toml // These interfaces were introduced in Go 1.2, so we add them manually when // compiling for Go 1.1. // TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here // so that Go 1.1 can be supported. type TextMarshaler interface { MarshalText() (text []byte, err error) } // TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined // here so that Go 1.1 can be supported. type TextUnmarshaler interface { UnmarshalText(text []byte) error } direnv-2.21.2/vendor/github.com/BurntSushi/toml/lex.go000066400000000000000000000533071361411000000226130ustar00rootroot00000000000000package toml import ( "fmt" "strings" "unicode" "unicode/utf8" ) type itemType int const ( itemError itemType = iota itemNIL // used in the parser to indicate no type itemEOF itemText itemString itemRawString itemMultilineString itemRawMultilineString itemBool itemInteger itemFloat itemDatetime itemArray // the start of an array itemArrayEnd itemTableStart itemTableEnd itemArrayTableStart itemArrayTableEnd itemKeyStart itemCommentStart itemInlineTableStart itemInlineTableEnd ) const ( eof = 0 comma = ',' tableStart = '[' tableEnd = ']' arrayTableStart = '[' arrayTableEnd = ']' tableSep = '.' keySep = '=' arrayStart = '[' arrayEnd = ']' commentStart = '#' stringStart = '"' stringEnd = '"' rawStringStart = '\'' rawStringEnd = '\'' inlineTableStart = '{' inlineTableEnd = '}' ) type stateFn func(lx *lexer) stateFn type lexer struct { input string start int pos int line int state stateFn items chan item // Allow for backing up up to three runes. // This is necessary because TOML contains 3-rune tokens (""" and '''). prevWidths [3]int nprev int // how many of prevWidths are in use // If we emit an eof, we can still back up, but it is not OK to call // next again. atEOF bool // A stack of state functions used to maintain context. // The idea is to reuse parts of the state machine in various places. // For example, values can appear at the top level or within arbitrarily // nested arrays. The last state on the stack is used after a value has // been lexed. Similarly for comments. stack []stateFn } type item struct { typ itemType val string line int } func (lx *lexer) nextItem() item { for { select { case item := <-lx.items: return item default: lx.state = lx.state(lx) } } } func lex(input string) *lexer { lx := &lexer{ input: input, state: lexTop, line: 1, items: make(chan item, 10), stack: make([]stateFn, 0, 10), } return lx } func (lx *lexer) push(state stateFn) { lx.stack = append(lx.stack, state) } func (lx *lexer) pop() stateFn { if len(lx.stack) == 0 { return lx.errorf("BUG in lexer: no states to pop") } last := lx.stack[len(lx.stack)-1] lx.stack = lx.stack[0 : len(lx.stack)-1] return last } func (lx *lexer) current() string { return lx.input[lx.start:lx.pos] } func (lx *lexer) emit(typ itemType) { lx.items <- item{typ, lx.current(), lx.line} lx.start = lx.pos } func (lx *lexer) emitTrim(typ itemType) { lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line} lx.start = lx.pos } func (lx *lexer) next() (r rune) { if lx.atEOF { panic("next called after EOF") } if lx.pos >= len(lx.input) { lx.atEOF = true return eof } if lx.input[lx.pos] == '\n' { lx.line++ } lx.prevWidths[2] = lx.prevWidths[1] lx.prevWidths[1] = lx.prevWidths[0] if lx.nprev < 3 { lx.nprev++ } r, w := utf8.DecodeRuneInString(lx.input[lx.pos:]) lx.prevWidths[0] = w lx.pos += w return r } // ignore skips over the pending input before this point. func (lx *lexer) ignore() { lx.start = lx.pos } // backup steps back one rune. Can be called only twice between calls to next. func (lx *lexer) backup() { if lx.atEOF { lx.atEOF = false return } if lx.nprev < 1 { panic("backed up too far") } w := lx.prevWidths[0] lx.prevWidths[0] = lx.prevWidths[1] lx.prevWidths[1] = lx.prevWidths[2] lx.nprev-- lx.pos -= w if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { lx.line-- } } // accept consumes the next rune if it's equal to `valid`. func (lx *lexer) accept(valid rune) bool { if lx.next() == valid { return true } lx.backup() return false } // peek returns but does not consume the next rune in the input. func (lx *lexer) peek() rune { r := lx.next() lx.backup() return r } // skip ignores all input that matches the given predicate. func (lx *lexer) skip(pred func(rune) bool) { for { r := lx.next() if pred(r) { continue } lx.backup() lx.ignore() return } } // errorf stops all lexing by emitting an error and returning `nil`. // Note that any value that is a character is escaped if it's a special // character (newlines, tabs, etc.). func (lx *lexer) errorf(format string, values ...interface{}) stateFn { lx.items <- item{ itemError, fmt.Sprintf(format, values...), lx.line, } return nil } // lexTop consumes elements at the top level of TOML data. func lexTop(lx *lexer) stateFn { r := lx.next() if isWhitespace(r) || isNL(r) { return lexSkip(lx, lexTop) } switch r { case commentStart: lx.push(lexTop) return lexCommentStart case tableStart: return lexTableStart case eof: if lx.pos > lx.start { return lx.errorf("unexpected EOF") } lx.emit(itemEOF) return nil } // At this point, the only valid item can be a key, so we back up // and let the key lexer do the rest. lx.backup() lx.push(lexTopEnd) return lexKeyStart } // lexTopEnd is entered whenever a top-level item has been consumed. (A value // or a table.) It must see only whitespace, and will turn back to lexTop // upon a newline. If it sees EOF, it will quit the lexer successfully. func lexTopEnd(lx *lexer) stateFn { r := lx.next() switch { case r == commentStart: // a comment will read to a newline for us. lx.push(lexTop) return lexCommentStart case isWhitespace(r): return lexTopEnd case isNL(r): lx.ignore() return lexTop case r == eof: lx.emit(itemEOF) return nil } return lx.errorf("expected a top-level item to end with a newline, "+ "comment, or EOF, but got %q instead", r) } // lexTable lexes the beginning of a table. Namely, it makes sure that // it starts with a character other than '.' and ']'. // It assumes that '[' has already been consumed. // It also handles the case that this is an item in an array of tables. // e.g., '[[name]]'. func lexTableStart(lx *lexer) stateFn { if lx.peek() == arrayTableStart { lx.next() lx.emit(itemArrayTableStart) lx.push(lexArrayTableEnd) } else { lx.emit(itemTableStart) lx.push(lexTableEnd) } return lexTableNameStart } func lexTableEnd(lx *lexer) stateFn { lx.emit(itemTableEnd) return lexTopEnd } func lexArrayTableEnd(lx *lexer) stateFn { if r := lx.next(); r != arrayTableEnd { return lx.errorf("expected end of table array name delimiter %q, "+ "but got %q instead", arrayTableEnd, r) } lx.emit(itemArrayTableEnd) return lexTopEnd } func lexTableNameStart(lx *lexer) stateFn { lx.skip(isWhitespace) switch r := lx.peek(); { case r == tableEnd || r == eof: return lx.errorf("unexpected end of table name " + "(table names cannot be empty)") case r == tableSep: return lx.errorf("unexpected table separator " + "(table names cannot be empty)") case r == stringStart || r == rawStringStart: lx.ignore() lx.push(lexTableNameEnd) return lexValue // reuse string lexing default: return lexBareTableName } } // lexBareTableName lexes the name of a table. It assumes that at least one // valid character for the table has already been read. func lexBareTableName(lx *lexer) stateFn { r := lx.next() if isBareKeyChar(r) { return lexBareTableName } lx.backup() lx.emit(itemText) return lexTableNameEnd } // lexTableNameEnd reads the end of a piece of a table name, optionally // consuming whitespace. func lexTableNameEnd(lx *lexer) stateFn { lx.skip(isWhitespace) switch r := lx.next(); { case isWhitespace(r): return lexTableNameEnd case r == tableSep: lx.ignore() return lexTableNameStart case r == tableEnd: return lx.pop() default: return lx.errorf("expected '.' or ']' to end table name, "+ "but got %q instead", r) } } // lexKeyStart consumes a key name up until the first non-whitespace character. // lexKeyStart will ignore whitespace. func lexKeyStart(lx *lexer) stateFn { r := lx.peek() switch { case r == keySep: return lx.errorf("unexpected key separator %q", keySep) case isWhitespace(r) || isNL(r): lx.next() return lexSkip(lx, lexKeyStart) case r == stringStart || r == rawStringStart: lx.ignore() lx.emit(itemKeyStart) lx.push(lexKeyEnd) return lexValue // reuse string lexing default: lx.ignore() lx.emit(itemKeyStart) return lexBareKey } } // lexBareKey consumes the text of a bare key. Assumes that the first character // (which is not whitespace) has not yet been consumed. func lexBareKey(lx *lexer) stateFn { switch r := lx.next(); { case isBareKeyChar(r): return lexBareKey case isWhitespace(r): lx.backup() lx.emit(itemText) return lexKeyEnd case r == keySep: lx.backup() lx.emit(itemText) return lexKeyEnd default: return lx.errorf("bare keys cannot contain %q", r) } } // lexKeyEnd consumes the end of a key and trims whitespace (up to the key // separator). func lexKeyEnd(lx *lexer) stateFn { switch r := lx.next(); { case r == keySep: return lexSkip(lx, lexValue) case isWhitespace(r): return lexSkip(lx, lexKeyEnd) default: return lx.errorf("expected key separator %q, but got %q instead", keySep, r) } } // lexValue starts the consumption of a value anywhere a value is expected. // lexValue will ignore whitespace. // After a value is lexed, the last state on the next is popped and returned. func lexValue(lx *lexer) stateFn { // We allow whitespace to precede a value, but NOT newlines. // In array syntax, the array states are responsible for ignoring newlines. r := lx.next() switch { case isWhitespace(r): return lexSkip(lx, lexValue) case isDigit(r): lx.backup() // avoid an extra state and use the same as above return lexNumberOrDateStart } switch r { case arrayStart: lx.ignore() lx.emit(itemArray) return lexArrayValue case inlineTableStart: lx.ignore() lx.emit(itemInlineTableStart) return lexInlineTableValue case stringStart: if lx.accept(stringStart) { if lx.accept(stringStart) { lx.ignore() // Ignore """ return lexMultilineString } lx.backup() } lx.ignore() // ignore the '"' return lexString case rawStringStart: if lx.accept(rawStringStart) { if lx.accept(rawStringStart) { lx.ignore() // Ignore """ return lexMultilineRawString } lx.backup() } lx.ignore() // ignore the "'" return lexRawString case '+', '-': return lexNumberStart case '.': // special error case, be kind to users return lx.errorf("floats must start with a digit, not '.'") } if unicode.IsLetter(r) { // Be permissive here; lexBool will give a nice error if the // user wrote something like // x = foo // (i.e. not 'true' or 'false' but is something else word-like.) lx.backup() return lexBool } return lx.errorf("expected value but found %q instead", r) } // lexArrayValue consumes one value in an array. It assumes that '[' or ',' // have already been consumed. All whitespace and newlines are ignored. func lexArrayValue(lx *lexer) stateFn { r := lx.next() switch { case isWhitespace(r) || isNL(r): return lexSkip(lx, lexArrayValue) case r == commentStart: lx.push(lexArrayValue) return lexCommentStart case r == comma: return lx.errorf("unexpected comma") case r == arrayEnd: // NOTE(caleb): The spec isn't clear about whether you can have // a trailing comma or not, so we'll allow it. return lexArrayEnd } lx.backup() lx.push(lexArrayValueEnd) return lexValue } // lexArrayValueEnd consumes everything between the end of an array value and // the next value (or the end of the array): it ignores whitespace and newlines // and expects either a ',' or a ']'. func lexArrayValueEnd(lx *lexer) stateFn { r := lx.next() switch { case isWhitespace(r) || isNL(r): return lexSkip(lx, lexArrayValueEnd) case r == commentStart: lx.push(lexArrayValueEnd) return lexCommentStart case r == comma: lx.ignore() return lexArrayValue // move on to the next value case r == arrayEnd: return lexArrayEnd } return lx.errorf( "expected a comma or array terminator %q, but got %q instead", arrayEnd, r, ) } // lexArrayEnd finishes the lexing of an array. // It assumes that a ']' has just been consumed. func lexArrayEnd(lx *lexer) stateFn { lx.ignore() lx.emit(itemArrayEnd) return lx.pop() } // lexInlineTableValue consumes one key/value pair in an inline table. // It assumes that '{' or ',' have already been consumed. Whitespace is ignored. func lexInlineTableValue(lx *lexer) stateFn { r := lx.next() switch { case isWhitespace(r): return lexSkip(lx, lexInlineTableValue) case isNL(r): return lx.errorf("newlines not allowed within inline tables") case r == commentStart: lx.push(lexInlineTableValue) return lexCommentStart case r == comma: return lx.errorf("unexpected comma") case r == inlineTableEnd: return lexInlineTableEnd } lx.backup() lx.push(lexInlineTableValueEnd) return lexKeyStart } // lexInlineTableValueEnd consumes everything between the end of an inline table // key/value pair and the next pair (or the end of the table): // it ignores whitespace and expects either a ',' or a '}'. func lexInlineTableValueEnd(lx *lexer) stateFn { r := lx.next() switch { case isWhitespace(r): return lexSkip(lx, lexInlineTableValueEnd) case isNL(r): return lx.errorf("newlines not allowed within inline tables") case r == commentStart: lx.push(lexInlineTableValueEnd) return lexCommentStart case r == comma: lx.ignore() return lexInlineTableValue case r == inlineTableEnd: return lexInlineTableEnd } return lx.errorf("expected a comma or an inline table terminator %q, "+ "but got %q instead", inlineTableEnd, r) } // lexInlineTableEnd finishes the lexing of an inline table. // It assumes that a '}' has just been consumed. func lexInlineTableEnd(lx *lexer) stateFn { lx.ignore() lx.emit(itemInlineTableEnd) return lx.pop() } // lexString consumes the inner contents of a string. It assumes that the // beginning '"' has already been consumed and ignored. func lexString(lx *lexer) stateFn { r := lx.next() switch { case r == eof: return lx.errorf("unexpected EOF") case isNL(r): return lx.errorf("strings cannot contain newlines") case r == '\\': lx.push(lexString) return lexStringEscape case r == stringEnd: lx.backup() lx.emit(itemString) lx.next() lx.ignore() return lx.pop() } return lexString } // lexMultilineString consumes the inner contents of a string. It assumes that // the beginning '"""' has already been consumed and ignored. func lexMultilineString(lx *lexer) stateFn { switch lx.next() { case eof: return lx.errorf("unexpected EOF") case '\\': return lexMultilineStringEscape case stringEnd: if lx.accept(stringEnd) { if lx.accept(stringEnd) { lx.backup() lx.backup() lx.backup() lx.emit(itemMultilineString) lx.next() lx.next() lx.next() lx.ignore() return lx.pop() } lx.backup() } } return lexMultilineString } // lexRawString consumes a raw string. Nothing can be escaped in such a string. // It assumes that the beginning "'" has already been consumed and ignored. func lexRawString(lx *lexer) stateFn { r := lx.next() switch { case r == eof: return lx.errorf("unexpected EOF") case isNL(r): return lx.errorf("strings cannot contain newlines") case r == rawStringEnd: lx.backup() lx.emit(itemRawString) lx.next() lx.ignore() return lx.pop() } return lexRawString } // lexMultilineRawString consumes a raw string. Nothing can be escaped in such // a string. It assumes that the beginning "'''" has already been consumed and // ignored. func lexMultilineRawString(lx *lexer) stateFn { switch lx.next() { case eof: return lx.errorf("unexpected EOF") case rawStringEnd: if lx.accept(rawStringEnd) { if lx.accept(rawStringEnd) { lx.backup() lx.backup() lx.backup() lx.emit(itemRawMultilineString) lx.next() lx.next() lx.next() lx.ignore() return lx.pop() } lx.backup() } } return lexMultilineRawString } // lexMultilineStringEscape consumes an escaped character. It assumes that the // preceding '\\' has already been consumed. func lexMultilineStringEscape(lx *lexer) stateFn { // Handle the special case first: if isNL(lx.next()) { return lexMultilineString } lx.backup() lx.push(lexMultilineString) return lexStringEscape(lx) } func lexStringEscape(lx *lexer) stateFn { r := lx.next() switch r { case 'b': fallthrough case 't': fallthrough case 'n': fallthrough case 'f': fallthrough case 'r': fallthrough case '"': fallthrough case '\\': return lx.pop() case 'u': return lexShortUnicodeEscape case 'U': return lexLongUnicodeEscape } return lx.errorf("invalid escape character %q; only the following "+ "escape characters are allowed: "+ `\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r) } func lexShortUnicodeEscape(lx *lexer) stateFn { var r rune for i := 0; i < 4; i++ { r = lx.next() if !isHexadecimal(r) { return lx.errorf(`expected four hexadecimal digits after '\u', `+ "but got %q instead", lx.current()) } } return lx.pop() } func lexLongUnicodeEscape(lx *lexer) stateFn { var r rune for i := 0; i < 8; i++ { r = lx.next() if !isHexadecimal(r) { return lx.errorf(`expected eight hexadecimal digits after '\U', `+ "but got %q instead", lx.current()) } } return lx.pop() } // lexNumberOrDateStart consumes either an integer, a float, or datetime. func lexNumberOrDateStart(lx *lexer) stateFn { r := lx.next() if isDigit(r) { return lexNumberOrDate } switch r { case '_': return lexNumber case 'e', 'E': return lexFloat case '.': return lx.errorf("floats must start with a digit, not '.'") } return lx.errorf("expected a digit but got %q", r) } // lexNumberOrDate consumes either an integer, float or datetime. func lexNumberOrDate(lx *lexer) stateFn { r := lx.next() if isDigit(r) { return lexNumberOrDate } switch r { case '-': return lexDatetime case '_': return lexNumber case '.', 'e', 'E': return lexFloat } lx.backup() lx.emit(itemInteger) return lx.pop() } // lexDatetime consumes a Datetime, to a first approximation. // The parser validates that it matches one of the accepted formats. func lexDatetime(lx *lexer) stateFn { r := lx.next() if isDigit(r) { return lexDatetime } switch r { case '-', 'T', ':', '.', 'Z', '+': return lexDatetime } lx.backup() lx.emit(itemDatetime) return lx.pop() } // lexNumberStart consumes either an integer or a float. It assumes that a sign // has already been read, but that *no* digits have been consumed. // lexNumberStart will move to the appropriate integer or float states. func lexNumberStart(lx *lexer) stateFn { // We MUST see a digit. Even floats have to start with a digit. r := lx.next() if !isDigit(r) { if r == '.' { return lx.errorf("floats must start with a digit, not '.'") } return lx.errorf("expected a digit but got %q", r) } return lexNumber } // lexNumber consumes an integer or a float after seeing the first digit. func lexNumber(lx *lexer) stateFn { r := lx.next() if isDigit(r) { return lexNumber } switch r { case '_': return lexNumber case '.', 'e', 'E': return lexFloat } lx.backup() lx.emit(itemInteger) return lx.pop() } // lexFloat consumes the elements of a float. It allows any sequence of // float-like characters, so floats emitted by the lexer are only a first // approximation and must be validated by the parser. func lexFloat(lx *lexer) stateFn { r := lx.next() if isDigit(r) { return lexFloat } switch r { case '_', '.', '-', '+', 'e', 'E': return lexFloat } lx.backup() lx.emit(itemFloat) return lx.pop() } // lexBool consumes a bool string: 'true' or 'false. func lexBool(lx *lexer) stateFn { var rs []rune for { r := lx.next() if !unicode.IsLetter(r) { lx.backup() break } rs = append(rs, r) } s := string(rs) switch s { case "true", "false": lx.emit(itemBool) return lx.pop() } return lx.errorf("expected value but found %q instead", s) } // lexCommentStart begins the lexing of a comment. It will emit // itemCommentStart and consume no characters, passing control to lexComment. func lexCommentStart(lx *lexer) stateFn { lx.ignore() lx.emit(itemCommentStart) return lexComment } // lexComment lexes an entire comment. It assumes that '#' has been consumed. // It will consume *up to* the first newline character, and pass control // back to the last state on the stack. func lexComment(lx *lexer) stateFn { r := lx.peek() if isNL(r) || r == eof { lx.emit(itemText) return lx.pop() } lx.next() return lexComment } // lexSkip ignores all slurped input and moves on to the next state. func lexSkip(lx *lexer, nextState stateFn) stateFn { return func(lx *lexer) stateFn { lx.ignore() return nextState } } // isWhitespace returns true if `r` is a whitespace character according // to the spec. func isWhitespace(r rune) bool { return r == '\t' || r == ' ' } func isNL(r rune) bool { return r == '\n' || r == '\r' } func isDigit(r rune) bool { return r >= '0' && r <= '9' } func isHexadecimal(r rune) bool { return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') } func isBareKeyChar(r rune) bool { return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r == '-' } func (itype itemType) String() string { switch itype { case itemError: return "Error" case itemNIL: return "NIL" case itemEOF: return "EOF" case itemText: return "Text" case itemString, itemRawString, itemMultilineString, itemRawMultilineString: return "String" case itemBool: return "Bool" case itemInteger: return "Integer" case itemFloat: return "Float" case itemDatetime: return "DateTime" case itemTableStart: return "TableStart" case itemTableEnd: return "TableEnd" case itemKeyStart: return "KeyStart" case itemArray: return "Array" case itemArrayEnd: return "ArrayEnd" case itemCommentStart: return "CommentStart" } panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype))) } func (item item) String() string { return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) } direnv-2.21.2/vendor/github.com/BurntSushi/toml/parse.go000066400000000000000000000364501361411000000231350ustar00rootroot00000000000000package toml import ( "fmt" "strconv" "strings" "time" "unicode" "unicode/utf8" ) type parser struct { mapping map[string]interface{} types map[string]tomlType lx *lexer // A list of keys in the order that they appear in the TOML data. ordered []Key // the full key for the current hash in scope context Key // the base key name for everything except hashes currentKey string // rough approximation of line number approxLine int // A map of 'key.group.names' to whether they were created implicitly. implicits map[string]bool } type parseError string func (pe parseError) Error() string { return string(pe) } func parse(data string) (p *parser, err error) { defer func() { if r := recover(); r != nil { var ok bool if err, ok = r.(parseError); ok { return } panic(r) } }() p = &parser{ mapping: make(map[string]interface{}), types: make(map[string]tomlType), lx: lex(data), ordered: make([]Key, 0), implicits: make(map[string]bool), } for { item := p.next() if item.typ == itemEOF { break } p.topLevel(item) } return p, nil } func (p *parser) panicf(format string, v ...interface{}) { msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s", p.approxLine, p.current(), fmt.Sprintf(format, v...)) panic(parseError(msg)) } func (p *parser) next() item { it := p.lx.nextItem() if it.typ == itemError { p.panicf("%s", it.val) } return it } func (p *parser) bug(format string, v ...interface{}) { panic(fmt.Sprintf("BUG: "+format+"\n\n", v...)) } func (p *parser) expect(typ itemType) item { it := p.next() p.assertEqual(typ, it.typ) return it } func (p *parser) assertEqual(expected, got itemType) { if expected != got { p.bug("Expected '%s' but got '%s'.", expected, got) } } func (p *parser) topLevel(item item) { switch item.typ { case itemCommentStart: p.approxLine = item.line p.expect(itemText) case itemTableStart: kg := p.next() p.approxLine = kg.line var key Key for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() { key = append(key, p.keyString(kg)) } p.assertEqual(itemTableEnd, kg.typ) p.establishContext(key, false) p.setType("", tomlHash) p.ordered = append(p.ordered, key) case itemArrayTableStart: kg := p.next() p.approxLine = kg.line var key Key for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() { key = append(key, p.keyString(kg)) } p.assertEqual(itemArrayTableEnd, kg.typ) p.establishContext(key, true) p.setType("", tomlArrayHash) p.ordered = append(p.ordered, key) case itemKeyStart: kname := p.next() p.approxLine = kname.line p.currentKey = p.keyString(kname) val, typ := p.value(p.next()) p.setValue(p.currentKey, val) p.setType(p.currentKey, typ) p.ordered = append(p.ordered, p.context.add(p.currentKey)) p.currentKey = "" default: p.bug("Unexpected type at top level: %s", item.typ) } } // Gets a string for a key (or part of a key in a table name). func (p *parser) keyString(it item) string { switch it.typ { case itemText: return it.val case itemString, itemMultilineString, itemRawString, itemRawMultilineString: s, _ := p.value(it) return s.(string) default: p.bug("Unexpected key type: %s", it.typ) panic("unreachable") } } // value translates an expected value from the lexer into a Go value wrapped // as an empty interface. func (p *parser) value(it item) (interface{}, tomlType) { switch it.typ { case itemString: return p.replaceEscapes(it.val), p.typeOfPrimitive(it) case itemMultilineString: trimmed := stripFirstNewline(stripEscapedWhitespace(it.val)) return p.replaceEscapes(trimmed), p.typeOfPrimitive(it) case itemRawString: return it.val, p.typeOfPrimitive(it) case itemRawMultilineString: return stripFirstNewline(it.val), p.typeOfPrimitive(it) case itemBool: switch it.val { case "true": return true, p.typeOfPrimitive(it) case "false": return false, p.typeOfPrimitive(it) } p.bug("Expected boolean value, but got '%s'.", it.val) case itemInteger: if !numUnderscoresOK(it.val) { p.panicf("Invalid integer %q: underscores must be surrounded by digits", it.val) } val := strings.Replace(it.val, "_", "", -1) num, err := strconv.ParseInt(val, 10, 64) if err != nil { // Distinguish integer values. Normally, it'd be a bug if the lexer // provides an invalid integer, but it's possible that the number is // out of range of valid values (which the lexer cannot determine). // So mark the former as a bug but the latter as a legitimate user // error. if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange { p.panicf("Integer '%s' is out of the range of 64-bit "+ "signed integers.", it.val) } else { p.bug("Expected integer value, but got '%s'.", it.val) } } return num, p.typeOfPrimitive(it) case itemFloat: parts := strings.FieldsFunc(it.val, func(r rune) bool { switch r { case '.', 'e', 'E': return true } return false }) for _, part := range parts { if !numUnderscoresOK(part) { p.panicf("Invalid float %q: underscores must be "+ "surrounded by digits", it.val) } } if !numPeriodsOK(it.val) { // As a special case, numbers like '123.' or '1.e2', // which are valid as far as Go/strconv are concerned, // must be rejected because TOML says that a fractional // part consists of '.' followed by 1+ digits. p.panicf("Invalid float %q: '.' must be followed "+ "by one or more digits", it.val) } val := strings.Replace(it.val, "_", "", -1) num, err := strconv.ParseFloat(val, 64) if err != nil { if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange { p.panicf("Float '%s' is out of the range of 64-bit "+ "IEEE-754 floating-point numbers.", it.val) } else { p.panicf("Invalid float value: %q", it.val) } } return num, p.typeOfPrimitive(it) case itemDatetime: var t time.Time var ok bool var err error for _, format := range []string{ "2006-01-02T15:04:05Z07:00", "2006-01-02T15:04:05", "2006-01-02", } { t, err = time.ParseInLocation(format, it.val, time.Local) if err == nil { ok = true break } } if !ok { p.panicf("Invalid TOML Datetime: %q.", it.val) } return t, p.typeOfPrimitive(it) case itemArray: array := make([]interface{}, 0) types := make([]tomlType, 0) for it = p.next(); it.typ != itemArrayEnd; it = p.next() { if it.typ == itemCommentStart { p.expect(itemText) continue } val, typ := p.value(it) array = append(array, val) types = append(types, typ) } return array, p.typeOfArray(types) case itemInlineTableStart: var ( hash = make(map[string]interface{}) outerContext = p.context outerKey = p.currentKey ) p.context = append(p.context, p.currentKey) p.currentKey = "" for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() { if it.typ != itemKeyStart { p.bug("Expected key start but instead found %q, around line %d", it.val, p.approxLine) } if it.typ == itemCommentStart { p.expect(itemText) continue } // retrieve key k := p.next() p.approxLine = k.line kname := p.keyString(k) // retrieve value p.currentKey = kname val, typ := p.value(p.next()) // make sure we keep metadata up to date p.setType(kname, typ) p.ordered = append(p.ordered, p.context.add(p.currentKey)) hash[kname] = val } p.context = outerContext p.currentKey = outerKey return hash, tomlHash } p.bug("Unexpected value type: %s", it.typ) panic("unreachable") } // numUnderscoresOK checks whether each underscore in s is surrounded by // characters that are not underscores. func numUnderscoresOK(s string) bool { accept := false for _, r := range s { if r == '_' { if !accept { return false } accept = false continue } accept = true } return accept } // numPeriodsOK checks whether every period in s is followed by a digit. func numPeriodsOK(s string) bool { period := false for _, r := range s { if period && !isDigit(r) { return false } period = r == '.' } return !period } // establishContext sets the current context of the parser, // where the context is either a hash or an array of hashes. Which one is // set depends on the value of the `array` parameter. // // Establishing the context also makes sure that the key isn't a duplicate, and // will create implicit hashes automatically. func (p *parser) establishContext(key Key, array bool) { var ok bool // Always start at the top level and drill down for our context. hashContext := p.mapping keyContext := make(Key, 0) // We only need implicit hashes for key[0:-1] for _, k := range key[0 : len(key)-1] { _, ok = hashContext[k] keyContext = append(keyContext, k) // No key? Make an implicit hash and move on. if !ok { p.addImplicit(keyContext) hashContext[k] = make(map[string]interface{}) } // If the hash context is actually an array of tables, then set // the hash context to the last element in that array. // // Otherwise, it better be a table, since this MUST be a key group (by // virtue of it not being the last element in a key). switch t := hashContext[k].(type) { case []map[string]interface{}: hashContext = t[len(t)-1] case map[string]interface{}: hashContext = t default: p.panicf("Key '%s' was already created as a hash.", keyContext) } } p.context = keyContext if array { // If this is the first element for this array, then allocate a new // list of tables for it. k := key[len(key)-1] if _, ok := hashContext[k]; !ok { hashContext[k] = make([]map[string]interface{}, 0, 5) } // Add a new table. But make sure the key hasn't already been used // for something else. if hash, ok := hashContext[k].([]map[string]interface{}); ok { hashContext[k] = append(hash, make(map[string]interface{})) } else { p.panicf("Key '%s' was already created and cannot be used as "+ "an array.", keyContext) } } else { p.setValue(key[len(key)-1], make(map[string]interface{})) } p.context = append(p.context, key[len(key)-1]) } // setValue sets the given key to the given value in the current context. // It will make sure that the key hasn't already been defined, account for // implicit key groups. func (p *parser) setValue(key string, value interface{}) { var tmpHash interface{} var ok bool hash := p.mapping keyContext := make(Key, 0) for _, k := range p.context { keyContext = append(keyContext, k) if tmpHash, ok = hash[k]; !ok { p.bug("Context for key '%s' has not been established.", keyContext) } switch t := tmpHash.(type) { case []map[string]interface{}: // The context is a table of hashes. Pick the most recent table // defined as the current hash. hash = t[len(t)-1] case map[string]interface{}: hash = t default: p.bug("Expected hash to have type 'map[string]interface{}', but "+ "it has '%T' instead.", tmpHash) } } keyContext = append(keyContext, key) if _, ok := hash[key]; ok { // Typically, if the given key has already been set, then we have // to raise an error since duplicate keys are disallowed. However, // it's possible that a key was previously defined implicitly. In this // case, it is allowed to be redefined concretely. (See the // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.) // // But we have to make sure to stop marking it as an implicit. (So that // another redefinition provokes an error.) // // Note that since it has already been defined (as a hash), we don't // want to overwrite it. So our business is done. if p.isImplicit(keyContext) { p.removeImplicit(keyContext) return } // Otherwise, we have a concrete key trying to override a previous // key, which is *always* wrong. p.panicf("Key '%s' has already been defined.", keyContext) } hash[key] = value } // setType sets the type of a particular value at a given key. // It should be called immediately AFTER setValue. // // Note that if `key` is empty, then the type given will be applied to the // current context (which is either a table or an array of tables). func (p *parser) setType(key string, typ tomlType) { keyContext := make(Key, 0, len(p.context)+1) for _, k := range p.context { keyContext = append(keyContext, k) } if len(key) > 0 { // allow type setting for hashes keyContext = append(keyContext, key) } p.types[keyContext.String()] = typ } // addImplicit sets the given Key as having been created implicitly. func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = true } // removeImplicit stops tagging the given key as having been implicitly // created. func (p *parser) removeImplicit(key Key) { p.implicits[key.String()] = false } // isImplicit returns true if the key group pointed to by the key was created // implicitly. func (p *parser) isImplicit(key Key) bool { return p.implicits[key.String()] } // current returns the full key name of the current context. func (p *parser) current() string { if len(p.currentKey) == 0 { return p.context.String() } if len(p.context) == 0 { return p.currentKey } return fmt.Sprintf("%s.%s", p.context, p.currentKey) } func stripFirstNewline(s string) string { if len(s) == 0 || s[0] != '\n' { return s } return s[1:] } func stripEscapedWhitespace(s string) string { esc := strings.Split(s, "\\\n") if len(esc) > 1 { for i := 1; i < len(esc); i++ { esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace) } } return strings.Join(esc, "") } func (p *parser) replaceEscapes(str string) string { var replaced []rune s := []byte(str) r := 0 for r < len(s) { if s[r] != '\\' { c, size := utf8.DecodeRune(s[r:]) r += size replaced = append(replaced, c) continue } r += 1 if r >= len(s) { p.bug("Escape sequence at end of string.") return "" } switch s[r] { default: p.bug("Expected valid escape code after \\, but got %q.", s[r]) return "" case 'b': replaced = append(replaced, rune(0x0008)) r += 1 case 't': replaced = append(replaced, rune(0x0009)) r += 1 case 'n': replaced = append(replaced, rune(0x000A)) r += 1 case 'f': replaced = append(replaced, rune(0x000C)) r += 1 case 'r': replaced = append(replaced, rune(0x000D)) r += 1 case '"': replaced = append(replaced, rune(0x0022)) r += 1 case '\\': replaced = append(replaced, rune(0x005C)) r += 1 case 'u': // At this point, we know we have a Unicode escape of the form // `uXXXX` at [r, r+5). (Because the lexer guarantees this // for us.) escaped := p.asciiEscapeToUnicode(s[r+1 : r+5]) replaced = append(replaced, escaped) r += 5 case 'U': // At this point, we know we have a Unicode escape of the form // `uXXXX` at [r, r+9). (Because the lexer guarantees this // for us.) escaped := p.asciiEscapeToUnicode(s[r+1 : r+9]) replaced = append(replaced, escaped) r += 9 } } return string(replaced) } func (p *parser) asciiEscapeToUnicode(bs []byte) rune { s := string(bs) hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) if err != nil { p.bug("Could not parse '%s' as a hexadecimal number, but the "+ "lexer claims it's OK: %s", s, err) } if !utf8.ValidRune(rune(hex)) { p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s) } return rune(hex) } func isStringType(ty itemType) bool { return ty == itemString || ty == itemMultilineString || ty == itemRawString || ty == itemRawMultilineString } direnv-2.21.2/vendor/github.com/BurntSushi/toml/session.vim000066400000000000000000000000671361411000000236670ustar00rootroot00000000000000au BufWritePost *.go silent!make tags > /dev/null 2>&1 direnv-2.21.2/vendor/github.com/BurntSushi/toml/type_check.go000066400000000000000000000047021361411000000241340ustar00rootroot00000000000000package toml // tomlType represents any Go type that corresponds to a TOML type. // While the first draft of the TOML spec has a simplistic type system that // probably doesn't need this level of sophistication, we seem to be militating // toward adding real composite types. type tomlType interface { typeString() string } // typeEqual accepts any two types and returns true if they are equal. func typeEqual(t1, t2 tomlType) bool { if t1 == nil || t2 == nil { return false } return t1.typeString() == t2.typeString() } func typeIsHash(t tomlType) bool { return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) } type tomlBaseType string func (btype tomlBaseType) typeString() string { return string(btype) } func (btype tomlBaseType) String() string { return btype.typeString() } var ( tomlInteger tomlBaseType = "Integer" tomlFloat tomlBaseType = "Float" tomlDatetime tomlBaseType = "Datetime" tomlString tomlBaseType = "String" tomlBool tomlBaseType = "Bool" tomlArray tomlBaseType = "Array" tomlHash tomlBaseType = "Hash" tomlArrayHash tomlBaseType = "ArrayHash" ) // typeOfPrimitive returns a tomlType of any primitive value in TOML. // Primitive values are: Integer, Float, Datetime, String and Bool. // // Passing a lexer item other than the following will cause a BUG message // to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. func (p *parser) typeOfPrimitive(lexItem item) tomlType { switch lexItem.typ { case itemInteger: return tomlInteger case itemFloat: return tomlFloat case itemDatetime: return tomlDatetime case itemString: return tomlString case itemMultilineString: return tomlString case itemRawString: return tomlString case itemRawMultilineString: return tomlString case itemBool: return tomlBool } p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) panic("unreachable") } // typeOfArray returns a tomlType for an array given a list of types of its // values. // // In the current spec, if an array is homogeneous, then its type is always // "Array". If the array is not homogeneous, an error is generated. func (p *parser) typeOfArray(types []tomlType) tomlType { // Empty arrays are cool. if len(types) == 0 { return tomlArray } theType := types[0] for _, t := range types[1:] { if !typeEqual(theType, t) { p.panicf("Array contains values of type '%s' and '%s', but "+ "arrays must be homogeneous.", theType, t) } } return tomlArray } direnv-2.21.2/vendor/github.com/BurntSushi/toml/type_fields.go000066400000000000000000000145071361411000000243310ustar00rootroot00000000000000package toml // Struct field handling is adapted from code in encoding/json: // // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the Go distribution. import ( "reflect" "sort" "sync" ) // A field represents a single field found in a struct. type field struct { name string // the name of the field (`toml` tag included) tag bool // whether field has a `toml` tag index []int // represents the depth of an anonymous field typ reflect.Type // the type of the field } // byName sorts field by name, breaking ties with depth, // then breaking ties with "name came from toml tag", then // breaking ties with index sequence. type byName []field func (x byName) Len() int { return len(x) } func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byName) Less(i, j int) bool { if x[i].name != x[j].name { return x[i].name < x[j].name } if len(x[i].index) != len(x[j].index) { return len(x[i].index) < len(x[j].index) } if x[i].tag != x[j].tag { return x[i].tag } return byIndex(x).Less(i, j) } // byIndex sorts field by index sequence. type byIndex []field func (x byIndex) Len() int { return len(x) } func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byIndex) Less(i, j int) bool { for k, xik := range x[i].index { if k >= len(x[j].index) { return false } if xik != x[j].index[k] { return xik < x[j].index[k] } } return len(x[i].index) < len(x[j].index) } // typeFields returns a list of fields that TOML should recognize for the given // type. The algorithm is breadth-first search over the set of structs to // include - the top struct and then any reachable anonymous structs. func typeFields(t reflect.Type) []field { // Anonymous fields to explore at the current level and the next. current := []field{} next := []field{{typ: t}} // Count of queued names for current level and the next. count := map[reflect.Type]int{} nextCount := map[reflect.Type]int{} // Types already visited at an earlier level. visited := map[reflect.Type]bool{} // Fields found. var fields []field for len(next) > 0 { current, next = next, current[:0] count, nextCount = nextCount, map[reflect.Type]int{} for _, f := range current { if visited[f.typ] { continue } visited[f.typ] = true // Scan f.typ for fields to include. for i := 0; i < f.typ.NumField(); i++ { sf := f.typ.Field(i) if sf.PkgPath != "" && !sf.Anonymous { // unexported continue } opts := getOptions(sf.Tag) if opts.skip { continue } index := make([]int, len(f.index)+1) copy(index, f.index) index[len(f.index)] = i ft := sf.Type if ft.Name() == "" && ft.Kind() == reflect.Ptr { // Follow pointer. ft = ft.Elem() } // Record found field and index sequence. if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { tagged := opts.name != "" name := opts.name if name == "" { name = sf.Name } fields = append(fields, field{name, tagged, index, ft}) if count[f.typ] > 1 { // If there were multiple instances, add a second, // so that the annihilation code will see a duplicate. // It only cares about the distinction between 1 or 2, // so don't bother generating any more copies. fields = append(fields, fields[len(fields)-1]) } continue } // Record new anonymous struct to explore in next round. nextCount[ft]++ if nextCount[ft] == 1 { f := field{name: ft.Name(), index: index, typ: ft} next = append(next, f) } } } } sort.Sort(byName(fields)) // Delete all fields that are hidden by the Go rules for embedded fields, // except that fields with TOML tags are promoted. // The fields are sorted in primary order of name, secondary order // of field index length. Loop over names; for each name, delete // hidden fields by choosing the one dominant field that survives. out := fields[:0] for advance, i := 0, 0; i < len(fields); i += advance { // One iteration per name. // Find the sequence of fields with the name of this first field. fi := fields[i] name := fi.name for advance = 1; i+advance < len(fields); advance++ { fj := fields[i+advance] if fj.name != name { break } } if advance == 1 { // Only one field with this name out = append(out, fi) continue } dominant, ok := dominantField(fields[i : i+advance]) if ok { out = append(out, dominant) } } fields = out sort.Sort(byIndex(fields)) return fields } // dominantField looks through the fields, all of which are known to // have the same name, to find the single field that dominates the // others using Go's embedding rules, modified by the presence of // TOML tags. If there are multiple top-level fields, the boolean // will be false: This condition is an error in Go and we skip all // the fields. func dominantField(fields []field) (field, bool) { // The fields are sorted in increasing index-length order. The winner // must therefore be one with the shortest index length. Drop all // longer entries, which is easy: just truncate the slice. length := len(fields[0].index) tagged := -1 // Index of first tagged field. for i, f := range fields { if len(f.index) > length { fields = fields[:i] break } if f.tag { if tagged >= 0 { // Multiple tagged fields at the same level: conflict. // Return no field. return field{}, false } tagged = i } } if tagged >= 0 { return fields[tagged], true } // All remaining fields have the same length. If there's more than one, // we have a conflict (two fields named "X" at the same level) and we // return no field. if len(fields) > 1 { return field{}, false } return fields[0], true } var fieldCache struct { sync.RWMutex m map[reflect.Type][]field } // cachedTypeFields is like typeFields but uses a cache to avoid repeated work. func cachedTypeFields(t reflect.Type) []field { fieldCache.RLock() f := fieldCache.m[t] fieldCache.RUnlock() if f != nil { return f } // Compute fields without lock. // Might duplicate effort but won't hold other computations back. f = typeFields(t) if f == nil { f = []field{} } fieldCache.Lock() if fieldCache.m == nil { fieldCache.m = map[reflect.Type][]field{} } fieldCache.m[t] = f fieldCache.Unlock() return f } direnv-2.21.2/vendor/github.com/direnv/000077500000000000000000000000001361411000000176725ustar00rootroot00000000000000direnv-2.21.2/vendor/github.com/direnv/go-dotenv/000077500000000000000000000000001361411000000215745ustar00rootroot00000000000000direnv-2.21.2/vendor/github.com/direnv/go-dotenv/LICENSE.md000066400000000000000000000021721361411000000232020ustar00rootroot00000000000000(The MIT License) Copyright (c) 2017 zimbatm and [contributors](https://github.com/direnv/go-dotenv/graphs/contributors) 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. direnv-2.21.2/vendor/github.com/direnv/go-dotenv/README.md000066400000000000000000000012141361411000000230510ustar00rootroot00000000000000# go-dotenv Go parsing library for the dotenv format. There is no formal definition of the dotenv format but it has been introduced by https://github.com/bkeepers/dotenv which is thus canonical. This library is a port of that. This library was developed specifically for [direnv](https://direnv.net). ## Features * `k=v` format * bash `export k=v` format * yaml `k: v` format * comments ## Missing * support for variable expansion, probably needs API breakage ## Alternatives Some other good alternatives with various variations. * https://github.com/joho/godotenv * https://github.com/lazureykis/dotenv * https://github.com/subosito/gotenv direnv-2.21.2/vendor/github.com/direnv/go-dotenv/parse.go000066400000000000000000000061751361411000000232460ustar00rootroot00000000000000// Package dotenv implements the parsing of the .env format. // // There is no formal definition of the format but it has been introduced by // https://github.com/bkeepers/dotenv which is thus canonical. package dotenv import ( "fmt" "os" "regexp" "strings" ) // LINE is the regexp matching a single line const LINE = ` \A \s* (?:|#.*| # comment line (?:export\s+)? # optional export ([\w\.]+) # key (?:\s*=\s*|:\s+?) # separator ( # optional value begin '(?:\'|[^'])*' # single quoted value | # or "(?:\"|[^"])*" # double quoted value | # or [^#\n]+ # unquoted value )? # value end \s* (?:\#.*)? # optional comment ) \z ` var linesRe = regexp.MustCompile("[\\r\\n]+") var lineRe = regexp.MustCompile( regexp.MustCompile("\\s+").ReplaceAllLiteralString( regexp.MustCompile("\\s+# .*").ReplaceAllLiteralString(LINE, ""), "")) // Parse reads a string in the .env format and returns a map of the extracted key=values. // // Ported from https://github.com/bkeepers/dotenv/blob/84f33f48107c492c3a99bd41c1059e7b4c1bb67a/lib/dotenv/parser.rb func Parse(data string) (map[string]string, error) { var dotenv = make(map[string]string) for _, line := range linesRe.Split(data, -1) { if !lineRe.MatchString(line) { return nil, fmt.Errorf("invalid line: %s", line) } match := lineRe.FindStringSubmatch(line) // commented or empty line if len(match) == 0 { continue } if len(match[1]) == 0 { continue } key := match[1] value := match[2] err := parseValue(key, value, dotenv) if err != nil { return nil, fmt.Errorf("unable to parse %s, %s: %s", key, value, err) } } return dotenv, nil } // MustParse works the same as Parse but panics on error func MustParse(data string) map[string]string { env, err := Parse(data) if err != nil { panic(err) } return env } func parseValue(key string, value string, dotenv map[string]string) error { if len(value) <= 1 { dotenv[key] = value return nil } singleQuoted := false if value[0:1] == "'" && value[len(value)-1:] == "'" { // single-quoted string, do not expand singleQuoted = true value = value[1 : len(value)-1] } else if value[0:1] == `"` && value[len(value)-1:] == `"` { value = value[1 : len(value)-1] value = expandNewLines(value) value = unescapeCharacters(value) } if !singleQuoted { value = expandEnv(value, dotenv) } dotenv[key] = value return nil } var escRe = regexp.MustCompile("\\\\([^$])") func unescapeCharacters(value string) string { return escRe.ReplaceAllString(value, "$1") } func expandNewLines(value string) string { value = strings.Replace(value, "\\n", "\n", -1) value = strings.Replace(value, "\\r", "\r", -1) return value } func expandEnv(value string, dotenv map[string]string) string { expander := func(value string) string { expanded, found := lookupDotenv(value, dotenv) if found { return expanded } else { return os.Getenv(value) } } return os.Expand(value, expander) } func lookupDotenv(value string, dotenv map[string]string) (string, bool) { retval, ok := dotenv[value] return retval, ok } direnv-2.21.2/vendor/golang.org/000077500000000000000000000000001361411000000164015ustar00rootroot00000000000000direnv-2.21.2/vendor/golang.org/x/000077500000000000000000000000001361411000000166505ustar00rootroot00000000000000direnv-2.21.2/vendor/golang.org/x/mod/000077500000000000000000000000001361411000000174275ustar00rootroot00000000000000direnv-2.21.2/vendor/golang.org/x/mod/LICENSE000066400000000000000000000027071361411000000204420ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. direnv-2.21.2/vendor/golang.org/x/mod/PATENTS000066400000000000000000000024271361411000000204750ustar00rootroot00000000000000Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google 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, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. direnv-2.21.2/vendor/golang.org/x/mod/semver/000077500000000000000000000000001361411000000207305ustar00rootroot00000000000000direnv-2.21.2/vendor/golang.org/x/mod/semver/semver.go000066400000000000000000000210141361411000000225560ustar00rootroot00000000000000// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package semver implements comparison of semantic version strings. // In this package, semantic version strings must begin with a leading "v", // as in "v1.0.0". // // The general form of a semantic version string accepted by this package is // // vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] // // where square brackets indicate optional parts of the syntax; // MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; // PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers // using only alphanumeric characters and hyphens; and // all-numeric PRERELEASE identifiers must not have leading zeros. // // This package follows Semantic Versioning 2.0.0 (see semver.org) // with two exceptions. First, it requires the "v" prefix. Second, it recognizes // vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. package semver // parsed returns the parsed form of a semantic version string. type parsed struct { major string minor string patch string short string prerelease string build string err string } // IsValid reports whether v is a valid semantic version string. func IsValid(v string) bool { _, ok := parse(v) return ok } // Canonical returns the canonical formatting of the semantic version v. // It fills in any missing .MINOR or .PATCH and discards build metadata. // Two semantic versions compare equal only if their canonical formattings // are identical strings. // The canonical invalid semantic version is the empty string. func Canonical(v string) string { p, ok := parse(v) if !ok { return "" } if p.build != "" { return v[:len(v)-len(p.build)] } if p.short != "" { return v + p.short } return v } // Major returns the major version prefix of the semantic version v. // For example, Major("v2.1.0") == "v2". // If v is an invalid semantic version string, Major returns the empty string. func Major(v string) string { pv, ok := parse(v) if !ok { return "" } return v[:1+len(pv.major)] } // MajorMinor returns the major.minor version prefix of the semantic version v. // For example, MajorMinor("v2.1.0") == "v2.1". // If v is an invalid semantic version string, MajorMinor returns the empty string. func MajorMinor(v string) string { pv, ok := parse(v) if !ok { return "" } i := 1 + len(pv.major) if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { return v[:j] } return v[:i] + "." + pv.minor } // Prerelease returns the prerelease suffix of the semantic version v. // For example, Prerelease("v2.1.0-pre+meta") == "-pre". // If v is an invalid semantic version string, Prerelease returns the empty string. func Prerelease(v string) string { pv, ok := parse(v) if !ok { return "" } return pv.prerelease } // Build returns the build suffix of the semantic version v. // For example, Build("v2.1.0+meta") == "+meta". // If v is an invalid semantic version string, Build returns the empty string. func Build(v string) string { pv, ok := parse(v) if !ok { return "" } return pv.build } // Compare returns an integer comparing two versions according to // semantic version precedence. // The result will be 0 if v == w, -1 if v < w, or +1 if v > w. // // An invalid semantic version string is considered less than a valid one. // All invalid semantic version strings compare equal to each other. func Compare(v, w string) int { pv, ok1 := parse(v) pw, ok2 := parse(w) if !ok1 && !ok2 { return 0 } if !ok1 { return -1 } if !ok2 { return +1 } if c := compareInt(pv.major, pw.major); c != 0 { return c } if c := compareInt(pv.minor, pw.minor); c != 0 { return c } if c := compareInt(pv.patch, pw.patch); c != 0 { return c } return comparePrerelease(pv.prerelease, pw.prerelease) } // Max canonicalizes its arguments and then returns the version string // that compares greater. func Max(v, w string) string { v = Canonical(v) w = Canonical(w) if Compare(v, w) > 0 { return v } return w } func parse(v string) (p parsed, ok bool) { if v == "" || v[0] != 'v' { p.err = "missing v prefix" return } p.major, v, ok = parseInt(v[1:]) if !ok { p.err = "bad major version" return } if v == "" { p.minor = "0" p.patch = "0" p.short = ".0.0" return } if v[0] != '.' { p.err = "bad minor prefix" ok = false return } p.minor, v, ok = parseInt(v[1:]) if !ok { p.err = "bad minor version" return } if v == "" { p.patch = "0" p.short = ".0" return } if v[0] != '.' { p.err = "bad patch prefix" ok = false return } p.patch, v, ok = parseInt(v[1:]) if !ok { p.err = "bad patch version" return } if len(v) > 0 && v[0] == '-' { p.prerelease, v, ok = parsePrerelease(v) if !ok { p.err = "bad prerelease" return } } if len(v) > 0 && v[0] == '+' { p.build, v, ok = parseBuild(v) if !ok { p.err = "bad build" return } } if v != "" { p.err = "junk on end" ok = false return } ok = true return } func parseInt(v string) (t, rest string, ok bool) { if v == "" { return } if v[0] < '0' || '9' < v[0] { return } i := 1 for i < len(v) && '0' <= v[i] && v[i] <= '9' { i++ } if v[0] == '0' && i != 1 { return } return v[:i], v[i:], true } func parsePrerelease(v string) (t, rest string, ok bool) { // "A pre-release version MAY be denoted by appending a hyphen and // a series of dot separated identifiers immediately following the patch version. // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." if v == "" || v[0] != '-' { return } i := 1 start := 1 for i < len(v) && v[i] != '+' { if !isIdentChar(v[i]) && v[i] != '.' { return } if v[i] == '.' { if start == i || isBadNum(v[start:i]) { return } start = i + 1 } i++ } if start == i || isBadNum(v[start:i]) { return } return v[:i], v[i:], true } func parseBuild(v string) (t, rest string, ok bool) { if v == "" || v[0] != '+' { return } i := 1 start := 1 for i < len(v) { if !isIdentChar(v[i]) && v[i] != '.' { return } if v[i] == '.' { if start == i { return } start = i + 1 } i++ } if start == i { return } return v[:i], v[i:], true } func isIdentChar(c byte) bool { return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' } func isBadNum(v string) bool { i := 0 for i < len(v) && '0' <= v[i] && v[i] <= '9' { i++ } return i == len(v) && i > 1 && v[0] == '0' } func isNum(v string) bool { i := 0 for i < len(v) && '0' <= v[i] && v[i] <= '9' { i++ } return i == len(v) } func compareInt(x, y string) int { if x == y { return 0 } if len(x) < len(y) { return -1 } if len(x) > len(y) { return +1 } if x < y { return -1 } else { return +1 } } func comparePrerelease(x, y string) int { // "When major, minor, and patch are equal, a pre-release version has // lower precedence than a normal version. // Example: 1.0.0-alpha < 1.0.0. // Precedence for two pre-release versions with the same major, minor, // and patch version MUST be determined by comparing each dot separated // identifier from left to right until a difference is found as follows: // identifiers consisting of only digits are compared numerically and // identifiers with letters or hyphens are compared lexically in ASCII // sort order. Numeric identifiers always have lower precedence than // non-numeric identifiers. A larger set of pre-release fields has a // higher precedence than a smaller set, if all of the preceding // identifiers are equal. // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." if x == y { return 0 } if x == "" { return +1 } if y == "" { return -1 } for x != "" && y != "" { x = x[1:] // skip - or . y = y[1:] // skip - or . var dx, dy string dx, x = nextIdent(x) dy, y = nextIdent(y) if dx != dy { ix := isNum(dx) iy := isNum(dy) if ix != iy { if ix { return -1 } else { return +1 } } if ix { if len(dx) < len(dy) { return -1 } if len(dx) > len(dy) { return +1 } } if dx < dy { return -1 } else { return +1 } } } if x == "" { return -1 } else { return +1 } } func nextIdent(x string) (dx, rest string) { i := 0 for i < len(x) && x[i] != '.' { i++ } return x[:i], x[i:] } direnv-2.21.2/vendor/modules.txt000066400000000000000000000003161361411000000165550ustar00rootroot00000000000000# github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml # github.com/direnv/go-dotenv v0.0.0-20181227095604-4cce6d1a66f7 github.com/direnv/go-dotenv # golang.org/x/mod v0.2.0 golang.org/x/mod/semver direnv-2.21.2/version.go000066400000000000000000000000471361411000000150640ustar00rootroot00000000000000package main const Version = "2.21.2" direnv-2.21.2/version.txt000066400000000000000000000000071361411000000152720ustar00rootroot000000000000002.21.2 direnv-2.21.2/xdg/000077500000000000000000000000001361411000000136315ustar00rootroot00000000000000direnv-2.21.2/xdg/xdg.go000066400000000000000000000021031361411000000147360ustar00rootroot00000000000000// Package xdg is a minimal implementation of the XDG specification. // // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html package xdg import ( "path/filepath" ) // DataDir returns the data folder for the application func DataDir(env map[string]string, programName string) string { if env["XDG_DATA_HOME"] != "" { return filepath.Join(env["XDG_DATA_HOME"], programName) } else if env["HOME"] != "" { return filepath.Join(env["HOME"], ".local", "share", programName) } // In theory we could also read /etc/passwd and look for the home based on // the process' UID return "" } // ConfigDir returns the config folder for the application // // The XDG_CONFIG_DIRS case is not being handled func ConfigDir(env map[string]string, programName string) string { if env["XDG_CONFIG_HOME"] != "" { return filepath.Join(env["XDG_CONFIG_HOME"], programName) } else if env["HOME"] != "" { return filepath.Join(env["HOME"], ".config", programName) } // In theory we could also read /etc/passwd and look for the home based on // the process' UID return "" }