| /* |
| Copyright 2016 Google LLC |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| https://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| // Package differ determines how to invoke diff in the given environment. |
| package differ |
| |
| import ( |
| "fmt" |
| "os" |
| "os/exec" |
| "runtime" |
| "strings" |
| ) |
| |
| // Invocation of different diff commands, according to environment variables. |
| |
| // A Differ describes how to invoke diff. |
| type Differ struct { |
| Cmd string // command |
| MultiDiff bool // diff accepts list of multiple pairs |
| Args []string // accumulated arguments |
| } |
| |
| // run runs the given command with args. |
| func (d *Differ) run(command string, args ...string) error { |
| // The special diff command ":" means don't run anything. |
| if d.Cmd == ":" { |
| return nil |
| } |
| |
| // Pass args to bash and reference with $@ to avoid shell injection in args. |
| var cmd *exec.Cmd |
| if command == "FC" { |
| cmd = exec.Command(command, "/T") |
| } else { |
| cmd = exec.Command("/usr/bin/env", "bash", "-c", command+` "$@"`, "--") |
| } |
| cmd.Args = append(cmd.Args, args...) |
| cmd.Stdout = os.Stdout |
| cmd.Stderr = os.Stderr |
| if err := cmd.Start(); err != nil { |
| // Couldn't even start bash. Worth reporting. |
| return fmt.Errorf("buildifier: %s: %v", command, err) |
| } |
| |
| // Assume bash reported anything else worth reporting. |
| // As long as the program started (above), we don't care about the |
| // exact exit status. In the most common case, the diff command |
| // will exit 1, because there are diffs, causing bash to exit 1. |
| return cmd.Wait() |
| } |
| |
| // Show diffs old and new. |
| // For a single-pair diff program, Show runs the diff program before returning. |
| // For a multi-pair diff program, Show records the pair for later use by Run. |
| func (d *Differ) Show(old, new string) error { |
| if !d.MultiDiff { |
| return d.run(d.Cmd, old, new) |
| } |
| |
| d.Args = append(d.Args, ":", old, new) |
| return nil |
| } |
| |
| // Run runs any pending diffs. |
| // For a single-pair diff program, Show already ran diff; Run is a no-op. |
| // For a multi-pair diff program, Run displays the diffs queued by Show. |
| func (d *Differ) Run() error { |
| if !d.MultiDiff { |
| return nil |
| } |
| |
| if len(d.Args) == 0 { |
| return nil |
| } |
| return d.run(d.Cmd, d.Args...) |
| } |
| |
| // Find returns the differ to use, using various environment variables. |
| func Find() (*Differ, bool) { |
| d := &Differ{} |
| deprecationWarning := false |
| if cmd := os.Getenv("BUILDIFIER_DIFF"); cmd != "" { |
| deprecationWarning = true |
| d.Cmd = cmd |
| } |
| |
| // Load MultiDiff setting from environment. |
| knowMultiDiff := false |
| if md := os.Getenv("BUILDIFIER_MULTIDIFF"); md == "0" || md == "1" { |
| deprecationWarning = true |
| d.MultiDiff = md == "1" |
| knowMultiDiff = true |
| } |
| |
| if d.Cmd != "" { |
| if !knowMultiDiff { |
| lower := strings.ToLower(d.Cmd) |
| d.MultiDiff = strings.Contains(lower, "tkdiff") && |
| isatty(1) && os.Getenv("DISPLAY") != "" |
| } |
| } else { |
| if !knowMultiDiff { |
| d.MultiDiff = isatty(1) && os.Getenv("DISPLAY") != "" |
| if d.MultiDiff { |
| deprecationWarning = true |
| } |
| } |
| if d.MultiDiff { |
| d.Cmd = "tkdiff" |
| } else { |
| if runtime.GOOS == "windows" { |
| deprecationWarning = true |
| d.Cmd = "FC" |
| } else { |
| d.Cmd = "diff --unified" |
| } |
| } |
| } |
| return d, deprecationWarning |
| } |