Skip to content

Commit beb48a7

Browse files
committed
Initial implementation of BASE64.LUA script.
1 parent 2ef10bc commit beb48a7

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

util/BASE64.LUA

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
2+
-- b64c = base64 characters
3+
-- dt = decode table
4+
local b64c, dt = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', {}
5+
for i = 1, #b64c do dt[b64c:sub(i,i)] = i - 1 end
6+
7+
---Encode a file into base64
8+
---@param file file The file to turn into base64
9+
---@return string The base64 string
10+
function base64_encode_file(file)
11+
12+
-- r = result
13+
-- l = length
14+
local r, l = {}, 0
15+
16+
while true do
17+
18+
-- c = chunk of 3 bytes
19+
local c = file:read(3)
20+
if not c or #c == 0 then break end
21+
22+
-- Pad chunk to 3 bytes if needed
23+
-- pa = padded
24+
-- p = pad length
25+
local p = 3 - #c
26+
for _ = 1, p do
27+
c = c .. '\0'
28+
end
29+
30+
-- Convert 3 bytes to 4 base64 characters
31+
local b1, b2, b3, c1, c2, c3, c4 = c:byte(1, 3)
32+
local n = (b1 << 16) + (b2 << 8) + b3
33+
34+
c1 = b64c:sub(((n >> 18) & 63) + 1, ((n >> 18) & 63) + 1)
35+
c2 = b64c:sub(((n >> 12) & 63) + 1, ((n >> 12) & 63) + 1)
36+
c3 = b64c:sub(((n >> 6) & 63) + 1, ((n >> 6) & 63) + 1)
37+
c4 = b64c:sub((n & 63) + 1, (n & 63) + 1)
38+
39+
-- Apply padding
40+
if p == 1 then
41+
c4 = '='
42+
elseif p == 2 then
43+
c3 = '='
44+
c4 = '='
45+
end
46+
47+
local en = c1 .. c2 .. c3 .. c4
48+
table.insert(r, en)
49+
50+
l = l + 4
51+
if l >= 76 then -- Standard base64 line length
52+
table.insert(r, '\n')
53+
l = 0
54+
end
55+
end
56+
57+
return table.concat(r)
58+
end
59+
60+
---Decode a file from base64
61+
---@param file file The file containing the base64 string to decode
62+
---@return string The decoded raw data
63+
function base64_decode_file(file)
64+
65+
-- r = result
66+
-- b = buffer
67+
local r, b = {}, ""
68+
69+
-- Read all data and remove whitespace/newlines
70+
while true do
71+
72+
-- l = line
73+
local l = file:read("l")
74+
75+
if not l then break end
76+
b = b .. l:gsub("[^" .. b64c .. "=]", "")
77+
end
78+
79+
-- Process 4 characters at a time
80+
for i = 1, #b, 4 do
81+
82+
-- c = chunk of 3 bytes
83+
--b1 = byte 1
84+
--b2 = byte 2
85+
--b3 = byte 3
86+
--b4 = byte 4
87+
--c1 = chunk 1
88+
--c2 = chunk 2
89+
--c3 = chunk 3
90+
local c, b1, b2, b3, b4, n, c1, c2, c3 = b:sub(i, i + 3)
91+
if #c < 4 then break end
92+
93+
b1, b2, b3, b4 = dt[c:sub(1,1)] or 0, dt[c:sub(2,2)] or 0, dt[c:sub(3,3)] or 0, dt[c:sub(4,4)] or 0
94+
n = (b1 << 18) + (b2 << 12) + (b3 << 6) + b4
95+
c1, c2, c3 = string.char((n >> 16) & 255), string.char((n >> 8) & 255), string.char(n & 255)
96+
97+
-- Handle padding
98+
if c:sub(4,4) == '=' then
99+
if c:sub(3,3) == '=' then
100+
table.insert(r, c1) -- Only first byte
101+
else
102+
table.insert(r, c1 .. c2) -- First two bytes
103+
end
104+
else
105+
table.insert(r, c1 .. c2 .. c3) -- All three bytes
106+
end
107+
end
108+
109+
return table.concat(r)
110+
end
111+
112+
if #arg < 1 or #arg > 2 then
113+
print([[Usage: lua base64.lua [-e|-d] [file]
114+
-e: encode (default)
115+
-d: decode")
116+
file: input file (default: stdin)]])
117+
os.exit(1)
118+
end
119+
120+
-- em = encode mode
121+
-- fn = file name
122+
-- f = file pointer
123+
local em, fn, f = 1
124+
125+
for _, v in ipairs(arg) do
126+
if v == "-d" then
127+
em = nil
128+
else
129+
fn = v
130+
end
131+
end
132+
133+
if fn then
134+
f, e = io.open(fn, "rb")
135+
if not f then
136+
print(e)
137+
os.exit(1)
138+
end
139+
else
140+
f, e = io.stdin
141+
end
142+
143+
if not f then print(e) exit(1) end
144+
145+
if em then
146+
local encoded = base64_encode_file(f)
147+
io.write(encoded)
148+
if not encoded:sub(-1):match('\n') then
149+
io.write('\n')
150+
end
151+
else
152+
io.write(base64_decode_file(f))
153+
end
154+
155+
-- Close file if it was opened
156+
if fn then
157+
f:close()
158+
end

0 commit comments

Comments
 (0)