-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathbase64.sh
executable file
·252 lines (236 loc) · 7.53 KB
/
base64.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#!/bin/sh
set -e -u -f
LC_ALL=C
__ALLOC=1 # Starting heap at 1 because 0 is the null pointer.
_malloc() { # $2 = object size
: $((_$__ALLOC = $2)) # Track object size
: $(($1 = $__ALLOC + 1))
: $((__ALLOC += $2 + 1))
}
defarr() { _malloc $1 $2; }
defarr _buf 1024
unpack_escaped_string() { # $1 = string, $2 = size (optional)
__str="$1"
# Allocates enough space for all characters, assuming that no character is escaped
_malloc __addr $((${2:-${#__str} + 1}))
__ptr=$__addr
__end=$((__ptr + ${2:-${#__str} + 1})) # End of allocated memory
while [ -n "$__str" ] ; do
case "$__str" in
'\'*)
__str="${__str#?}" # Remove the current char from $__str
case "$__str" in
'0'*) __c=0 ;;
'a'*) __c=7 ;;
'b'*) __c=8 ;;
'f'*) __c=12 ;;
'n'*) __c=10 ;;
'r'*) __c=13 ;;
't'*) __c=9 ;;
'v'*) __c=11 ;;
'\'*) __c=92 ;;
'"'*) __c=34 ;;
"'"*) __c=39 ;;
'?'*) __c=63 ;;
'$'*) __c=36 ;; # Not in C, used to escape variable expansion between double quotes
*) echo "invalid escape in string: $__str"; exit 1 ;;
esac
__str="${__str#?}" # Remove the current char from $__str
;;
*)
__c=$(printf "%d" "'${__str%"${__str#?}"}"); __c=$((__c > 0 ? __c : 256 + __c))
__str="${__str#?}" # Remove the current char from $__str
;;
esac
: $((_$__ptr = __c))
: $((__ptr += 1))
done
while [ $__ptr -le $__end ]; do
: $((_$__ptr = 0))
: $((__ptr += 1))
done
}
# Define a string, and return a reference to it in the varible taken as argument.
# If the variable is already defined, this function does nothing.
# Note that it's up to the caller to ensure that no 2 strings share the same variable.
defstr() { # $1 = variable name, $2 = string, $3 = size (optional)
set +u # Necessary to allow the variable to be empty
if [ $(($1)) -eq 0 ]; then
unpack_escaped_string "$2" $3
: $(($1 = __addr))
fi
set -u
}
defstr __str_0 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
_codes=$__str_0
: $((b3 = b2 = b1 = 0))
_encode() {
let b1; let b2; let b3
while [ 1 != 0 ]; do
_getchar b1
if [ $b1 -lt 0 ] ; then
break
fi
_getchar b2
printf \\$(((_$((_codes + (b1 >> 2))))/64))$(((_$((_codes + (b1 >> 2))))/8%8))$(((_$((_codes + (b1 >> 2))))%8))
if [ $b2 -lt 0 ] ; then
printf \\$(((_$((_codes + (0x3f & (b1 << 4)))))/64))$(((_$((_codes + (0x3f & (b1 << 4)))))/8%8))$(((_$((_codes + (0x3f & (b1 << 4)))))%8))
printf "="
printf "="
break
else
printf \\$(((_$((_codes + (0x3f & ((b1 << 4) | (b2 >> 4))))))/64))$(((_$((_codes + (0x3f & ((b1 << 4) | (b2 >> 4))))))/8%8))$(((_$((_codes + (0x3f & ((b1 << 4) | (b2 >> 4))))))%8))
_getchar b3
if [ $b3 -lt 0 ] ; then
printf \\$(((_$((_codes + (0x3f & (b2 << 2)))))/64))$(((_$((_codes + (0x3f & (b2 << 2)))))/8%8))$(((_$((_codes + (0x3f & (b2 << 2)))))%8))
printf "="
break
else
printf \\$(((_$((_codes + (0x3f & ((b2 << 2) | (b3 >> 6))))))/64))$(((_$((_codes + (0x3f & ((b2 << 2) | (b3 >> 6))))))/8%8))$(((_$((_codes + (0x3f & ((b2 << 2) | (b3 >> 6))))))%8))
printf \\$(((_$((_codes + (0x3f & b3))))/64))$(((_$((_codes + (0x3f & b3))))/8%8))$(((_$((_codes + (0x3f & b3))))%8))
fi
fi
done
printf \\$(((__NEWLINE__)/64))$(((__NEWLINE__)/8%8))$(((__NEWLINE__)%8))
endlet $1 b3 b2 b1
}
defarr _lut 256
: $((c = 0))
_get() {
let c
while _getchar c; [ $c -ge 0 ]; do
if [ $((c = _$((_lut + c)))) -ge 0 ] ; then
break
fi
done
: $(($1 = c))
endlet $1 c
}
: $((c4 = c3 = c2 = c1 = i = 0))
_decode() {
let i; let c1; let c2; let c3; let c4
i=0
while [ $i -lt 256 ]; do
: $((_$((_lut + i)) = -1))
: $((i += 1))
done
i=0
while [ $i -lt 64 ]; do
: $((_$((_lut + 0xff & _$((_codes + i)))) = i))
: $((i += 1))
done
while _get c1; [ $c1 -ge 0 ]; do
if _get c2; [ $c2 -lt 0 ] ; then
exit 1
fi
printf \\$((((c1 << 2) | (c2 >> 4))/64))$((((c1 << 2) | (c2 >> 4))/8%8))$((((c1 << 2) | (c2 >> 4))%8))
if _get c3; [ $c3 -lt 0 ] ; then
break
fi
printf \\$(((0xff & ((c2 << 4) | (c3 >> 2)))/64))$(((0xff & ((c2 << 4) | (c3 >> 2)))/8%8))$(((0xff & ((c2 << 4) | (c3 >> 2)))%8))
if _get c4; [ $c4 -lt 0 ] ; then
break
fi
printf \\$(((0xff & ((c3 << 6) | c4))/64))$(((0xff & ((c3 << 6) | c4))/8%8))$(((0xff & ((c3 << 6) | c4))%8))
done
endlet $1 c4 c3 c2 c1 i
}
: $((myargv = argc = 0))
_main() { let argc $2; let myargv $3
if [ $argc = 1 ] ; then
_encode __
elif [ $argc = 2 ] && [ $((_$((_$((myargv + 1)) + 0)))) = $__MINUS__ ] && [ $((_$((_$((myargv + 1)) + 1)))) = $__d__ ] && [ $((_$((_$((myargv + 1)) + 2)))) = $__NUL__ ] ; then
_decode __
else
exit 1
fi
: $(($1 = 0))
endlet $1 myargv argc
}
# Character constants
readonly __NUL__=0
readonly __NEWLINE__=10
readonly __MINUS__=45
readonly __d__=100
# Runtime library
__stdin_buf=
__stdin_line_ending=0 # Line ending, either -1 (EOF) or 10 ('\n')
_getchar() {
if [ -z "$__stdin_buf" ]; then # need to get next line when buffer empty
if [ $__stdin_line_ending != 0 ]; then # Line is empty, return line ending
: $(($1 = __stdin_line_ending))
__stdin_line_ending=0 # Reset line ending for next getchar call
return
fi
IFS= # don't split input
if read -r __stdin_buf ; then # read next line into $__stdin_buf
if [ -z "$__stdin_buf" ] ; then # an empty line implies a newline character
: $(($1 = 10)) # next getchar call will read next line
return
fi
__stdin_line_ending=10
else
if [ -z "$__stdin_buf" ] ; then # EOF reached when read fails
: $(($1 = -1))
return
else
__stdin_line_ending=-1
fi
fi
fi
__c=$(printf "%d" "'${__stdin_buf%"${__stdin_buf#?}"}"); __c=$((__c > 0 ? __c : 256 + __c))
: $(($1 = __c))
__stdin_buf="${__stdin_buf#?}" # remove the current char from $__stdin_buf
}
# Convert a Shell string to a C string
unpack_string() {
__str="$2"
_malloc $1 $((${#__str} + 1))
__ptr=$(($1))
while [ -n "$__str" ] ; do
# Remove first char from string
__tail="${__str#?}"
# Remove all but first char
__char="${__str%"$__tail"}"
# Convert char to ASCII
__c=$(printf "%d" "'$__char"); __c=$((__c > 0 ? __c : 256 + __c))
# Write character to memory
: $((_$__ptr = __c))
# Continue with rest of string
: $((__ptr += 1))
__str="$__tail"
done
: $((_$__ptr = 0))
}
make_argv() {
__argc=$1; shift;
_malloc __argv $__argc # Allocate enough space for all elements. No need to initialize.
__argv_ptr=$__argv
while [ $# -ge 1 ]; do
unpack_string _$__argv_ptr "$1"
: $((__argv_ptr += 1))
shift
done
}
# Local variables
__=0
__SP=0
let() { # $1: variable name, $2: value (optional)
: $((__SP += 1)) $((__$__SP=$1)) # Push
: $(($1=${2-0})) # Init
}
endlet() { # $1: return variable
# $2...: function local variables
__ret=$1 # Don't overwrite return value
: $((__tmp = $__ret))
while [ $# -ge 2 ]; do
: $(($2 = __$__SP)) $((__SP -= 1)); # Pop
shift;
done
: $(($__ret=__tmp)) # Restore return value
}
# Setup argc, argv
__argc_for_main=$(($# + 1))
make_argv $__argc_for_main "$0" "$@"; __argv_for_main=$__argv
__code=0; # Success exit code
_main __code $__argc_for_main $__argv_for_main; exit $__code