diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ca25cbb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Sergey Grebenshchikov + +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. \ No newline at end of file diff --git a/Makefile b/Makefile index 2bf27aa..8267474 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION = 1.0.1 +VERSION = 1.1.0 APP := subst PACKAGES := $(shell go list -f {{.Dir}} ./...) diff --git a/README.md b/README.md index c7e5eaf..56fe224 100644 --- a/README.md +++ b/README.md @@ -23,16 +23,16 @@ Or [download the binary](https://github.com/sgreben/subst/releases/latest) from ```bash # Linux -curl -LO https://github.com/sgreben/subst/releases/download/1.0.1/subst_1.0.1_linux_x86_64.zip -unzip subst_1.0.1_linux_x86_64.zip +curl -LO https://github.com/sgreben/subst/releases/download/1.1.0/subst_1.1.0_linux_x86_64.zip +unzip subst_1.1.0_linux_x86_64.zip # OS X -curl -LO https://github.com/sgreben/subst/releases/download/1.0.1/subst_1.0.1_osx_x86_64.zip -unzip subst_1.0.1_osx_x86_64.zip +curl -LO https://github.com/sgreben/subst/releases/download/1.1.0/subst_1.1.0_osx_x86_64.zip +unzip subst_1.1.0_osx_x86_64.zip # Windows -curl -LO https://github.com/sgreben/subst/releases/download/1.0.1/subst_1.0.1_windows_x86_64.zip -unzip subst_1.0.1_windows_x86_64.zip +curl -LO https://github.com/sgreben/subst/releases/download/1.1.0/subst_1.1.0_windows_x86_64.zip +unzip subst_1.1.0_windows_x86_64.zip ``` ## Use it @@ -90,6 +90,13 @@ $SHELL /bin/zsh ``` +To ignore occurrences of defined variables, you can define an escape string using the `-escape` flag: + +```shell +$ echo 'unescaped: $X, ${X}. escaped: \$X, \${X}.' | subst -escape '\' X=value +unescaped: value, value. escaped: \$X, \${X}. +``` + ## Comments Feel free to [leave a comment](https://github.com/sgreben/subst/issues/1) or create an issue. diff --git a/README.template.md b/README.template.md index 6d3917b..452c55b 100644 --- a/README.template.md +++ b/README.template.md @@ -90,6 +90,13 @@ $SHELL /bin/zsh ``` +To ignore occurrences of defined variables, you can define an escape string using the `-escape` flag: + +```shell +$ echo 'unescaped: $X, ${X}. escaped: \$X, \${X}.' | subst -escape '\' X=value +unescaped: value, value. escaped: \$X, \${X}. +``` + ## Comments Feel free to [leave a comment](https://github.com/sgreben/subst/issues/1) or create an issue. \ No newline at end of file diff --git a/expand.go b/expand.go new file mode 100644 index 0000000..b763f63 --- /dev/null +++ b/expand.go @@ -0,0 +1,77 @@ +package main + +// Modified copy of os/env.go. The Go LICENSE file is included below: + +// Copyright (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. + +// 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 LICENSE file. + +func expand(s, escape string, mapping func(string) string) string { + buf := make([]byte, 0, 2*len(s)) + i := 0 + shouldEscape := len(escape) > 0 + escaped := escape + "$" + for j := 0; j < len(s); j++ { + if shouldEscape { + if j+len(escaped) < len(s) && s[j:j+len(escaped)] == escaped { + j += len(escaped) + continue + } + } + if s[j] == '$' && j+1 < len(s) { + buf = append(buf, s[i:j]...) + name, w := variableName(s[j+1:]) + buf = append(buf, mapping(name)...) + j += w + i = j + 1 + } + } + return string(buf) + s[i:] +} + +func isAlphaNum(c uint8) bool { + return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' +} + +func variableName(s string) (string, int) { + if s[0] == '{' { + for i := 1; i < len(s); i++ { + if s[i] == '}' { + return s[1:i], i + 1 + } + } + return "", 1 + } + var i int + for i = 0; i < len(s) && isAlphaNum(s[i]); i++ { + } + return s[:i], i +} diff --git a/main.go b/main.go index ff658d0..17c478b 100644 --- a/main.go +++ b/main.go @@ -14,15 +14,18 @@ var definitions = make(map[string]string) var nonzeroExit = false var newline = []byte{'\n'} -var quiet bool -var unknownWarn = true -var unknownKey = enumVar{ - Choices: []string{ - unknownValueEmpty, - unknownValueIgnore, - unknownValueError, - }, -} +var ( + quiet bool + unknownWarn = true + escape string + unknownKey = enumVar{ + Choices: []string{ + unknownValueEmpty, + unknownValueIgnore, + unknownValueError, + }, + } +) const ( unknownValueEmpty = "empty" @@ -43,6 +46,7 @@ func parseDefinition(d string) (key, value string, err error) { func init() { log.SetOutput(os.Stderr) flag.BoolVar(&quiet, "q", false, "suppress all logs") + flag.StringVar(&escape, "escape", "", "set the escape string. if set, a '$' preceded by the escape string does not lead to expansion") flag.Var(&unknownKey, "unknown", "handling of unknown keys, one of [ignore empty error] (default ignore)") flag.Parse() if quiet { @@ -85,7 +89,8 @@ func main() { s.Split(bufio.ScanLines) for s.Scan() { line := s.Text() - _, err := os.Stdout.WriteString(os.Expand(line, subst)) + line = expand(line, escape, subst) + _, err := os.Stdout.WriteString(line) if err != nil { log.Println(err) }