|
| 1 | +package shenv |
| 2 | + |
| 3 | +import "fmt" |
| 4 | + |
| 5 | +type bash struct{} |
| 6 | + |
| 7 | +// Bash shell instance |
| 8 | +var Bash Shell = bash{} |
| 9 | + |
| 10 | +const bashHook = ` |
| 11 | +_devbox_hook() { |
| 12 | + local previous_exit_status=$?; |
| 13 | + trap -- '' SIGINT; |
| 14 | + eval "$(devbox shellenv --config {{ .ProjectDir }})"; |
| 15 | + trap - SIGINT; |
| 16 | + return $previous_exit_status; |
| 17 | +}; |
| 18 | +if ! [[ "${PROMPT_COMMAND:-}" =~ _devbox_hook ]]; then |
| 19 | + PROMPT_COMMAND="_devbox_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}" |
| 20 | +fi |
| 21 | +` |
| 22 | + |
| 23 | +func (sh bash) Hook() (string, error) { |
| 24 | + return bashHook, nil |
| 25 | +} |
| 26 | + |
| 27 | +func (sh bash) Export(e ShellExport) (out string) { |
| 28 | + for key, value := range e { |
| 29 | + if value == nil { |
| 30 | + out += sh.unset(key) |
| 31 | + } else { |
| 32 | + out += sh.export(key, *value) |
| 33 | + } |
| 34 | + } |
| 35 | + return out |
| 36 | +} |
| 37 | + |
| 38 | +func (sh bash) Dump(env Env) (out string) { |
| 39 | + for key, value := range env { |
| 40 | + out += sh.export(key, value) |
| 41 | + } |
| 42 | + return out |
| 43 | +} |
| 44 | + |
| 45 | +func (sh bash) export(key, value string) string { |
| 46 | + return "export " + sh.escape(key) + "=" + sh.escape(value) + ";" |
| 47 | +} |
| 48 | + |
| 49 | +func (sh bash) unset(key string) string { |
| 50 | + return "unset " + sh.escape(key) + ";" |
| 51 | +} |
| 52 | + |
| 53 | +func (sh bash) escape(str string) string { |
| 54 | + return BashEscape(str) |
| 55 | +} |
| 56 | + |
| 57 | +/* |
| 58 | + * Escaping |
| 59 | + */ |
| 60 | + |
| 61 | +// nolint |
| 62 | +const ( |
| 63 | + ACK = 6 |
| 64 | + TAB = 9 |
| 65 | + LF = 10 |
| 66 | + CR = 13 |
| 67 | + US = 31 |
| 68 | + SPACE = 32 |
| 69 | + AMPERSTAND = 38 |
| 70 | + SINGLE_QUOTE = 39 |
| 71 | + PLUS = 43 |
| 72 | + NINE = 57 |
| 73 | + QUESTION = 63 |
| 74 | + UPPERCASE_Z = 90 |
| 75 | + OPEN_BRACKET = 91 |
| 76 | + BACKSLASH = 92 |
| 77 | + UNDERSCORE = 95 |
| 78 | + CLOSE_BRACKET = 93 |
| 79 | + BACKTICK = 96 |
| 80 | + LOWERCASE_Z = 122 |
| 81 | + TILDA = 126 |
| 82 | + DEL = 127 |
| 83 | +) |
| 84 | + |
| 85 | +// https://github.com/solidsnack/shell-escape/blob/master/Text/ShellEscape/Bash.hs |
| 86 | +/* |
| 87 | +A Bash escaped string. The strings are wrapped in @$\'...\'@ if any |
| 88 | +bytes within them must be escaped; otherwise, they are left as is. |
| 89 | +Newlines and other control characters are represented as ANSI escape |
| 90 | +sequences. High bytes are represented as hex codes. Thus Bash escaped |
| 91 | +strings will always fit on one line and never contain non-ASCII bytes. |
| 92 | +*/ |
| 93 | +func BashEscape(str string) string { |
| 94 | + if str == "" { |
| 95 | + return "''" |
| 96 | + } |
| 97 | + // var too short |
| 98 | + //nolint:varnamelen |
| 99 | + in := []byte(str) |
| 100 | + out := "" |
| 101 | + i := 0 |
| 102 | + // var too short |
| 103 | + //nolint:varnamelen |
| 104 | + l := len(in) |
| 105 | + escape := false |
| 106 | + |
| 107 | + hex := func(char byte) { |
| 108 | + escape = true |
| 109 | + out += fmt.Sprintf("\\x%02x", char) |
| 110 | + } |
| 111 | + |
| 112 | + backslash := func(char byte) { |
| 113 | + escape = true |
| 114 | + out += string([]byte{BACKSLASH, char}) |
| 115 | + } |
| 116 | + |
| 117 | + escaped := func(str string) { |
| 118 | + escape = true |
| 119 | + out += str |
| 120 | + } |
| 121 | + |
| 122 | + quoted := func(char byte) { |
| 123 | + escape = true |
| 124 | + out += string([]byte{char}) |
| 125 | + } |
| 126 | + |
| 127 | + literal := func(char byte) { |
| 128 | + out += string([]byte{char}) |
| 129 | + } |
| 130 | + |
| 131 | + for i < l { |
| 132 | + char := in[i] |
| 133 | + switch { |
| 134 | + case char == ACK: |
| 135 | + hex(char) |
| 136 | + case char == TAB: |
| 137 | + escaped(`\t`) |
| 138 | + case char == LF: |
| 139 | + escaped(`\n`) |
| 140 | + case char == CR: |
| 141 | + escaped(`\r`) |
| 142 | + case char <= US: |
| 143 | + hex(char) |
| 144 | + case char <= AMPERSTAND: |
| 145 | + quoted(char) |
| 146 | + case char == SINGLE_QUOTE: |
| 147 | + backslash(char) |
| 148 | + case char <= PLUS: |
| 149 | + quoted(char) |
| 150 | + case char <= NINE: |
| 151 | + literal(char) |
| 152 | + case char <= QUESTION: |
| 153 | + quoted(char) |
| 154 | + case char <= UPPERCASE_Z: |
| 155 | + literal(char) |
| 156 | + case char == OPEN_BRACKET: |
| 157 | + quoted(char) |
| 158 | + case char == BACKSLASH: |
| 159 | + backslash(char) |
| 160 | + case char == UNDERSCORE: |
| 161 | + literal(char) |
| 162 | + case char <= CLOSE_BRACKET: |
| 163 | + quoted(char) |
| 164 | + case char <= BACKTICK: |
| 165 | + quoted(char) |
| 166 | + case char <= TILDA: |
| 167 | + quoted(char) |
| 168 | + case char == DEL: |
| 169 | + hex(char) |
| 170 | + default: |
| 171 | + hex(char) |
| 172 | + } |
| 173 | + i++ |
| 174 | + } |
| 175 | + |
| 176 | + if escape { |
| 177 | + out = "$'" + out + "'" |
| 178 | + } |
| 179 | + |
| 180 | + return out |
| 181 | +} |
0 commit comments