791 lines
21 KiB
Lua
Executable File
791 lines
21 KiB
Lua
Executable File
#!/usr/bin/env lua
|
|
|
|
local version = 'v1.0.0'
|
|
local socket = require("socket")
|
|
local urlparser = require("socket.url")
|
|
local separator = '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n'
|
|
local valid_types = {'0', '1', 'i', '7'}
|
|
local download_types = {I = 'IMG', h = 'HTM', ['9'] = 'BIN', g = 'GIF', s = 'SND'}
|
|
session = { tabs = { }, favorites = {} }
|
|
local save_file_location = string.format('%s/.stubbfaves', assert(os.getenv('HOME'), 'Unable to get home directory'))
|
|
local search_provider = 'gopher://gopher.floodgap.com:70/7/v2/vs'
|
|
|
|
|
|
|
|
--||__||--||__||--||__||--||__||--||__||--||__||--||__||--||
|
|
------------------------------------------------------------
|
|
-- Helper methods ------------------------------------------
|
|
------------------------------------------------------------
|
|
|
|
function string.split(str, sep)
|
|
assert(str and sep, 'sting.split requires both an input string and a field separator string')
|
|
local start = 1
|
|
local t = {}
|
|
while true do
|
|
local out
|
|
local point = string.find(str, sep, start)
|
|
if point == start then
|
|
out = ''
|
|
elseif not point then
|
|
out = string.sub(str, start)
|
|
else
|
|
out = string.sub(str, start, point - 1)
|
|
end
|
|
table.insert(t, out)
|
|
start = point and point + 1 or nil
|
|
if not start then
|
|
break
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
function table.out(t)
|
|
for i, v in pairs(t) do
|
|
print(i, string.format('%q', v))
|
|
end
|
|
end
|
|
|
|
|
|
-- Checks to see if an autoindexed table has a value
|
|
table.has = function(t, item)
|
|
for _, v in ipairs(t) do
|
|
if v == item then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
|
|
table.truncate = function(t, i)
|
|
while t[i] do
|
|
table.remove(t, i)
|
|
end
|
|
end
|
|
|
|
|
|
function invalid_key(key)
|
|
if key == 'enter' then io.write('\27[2A\27[200D\27[K\27[1A\27[200D\27[K')
|
|
elseif key == 'other' then io.write('\27[1A\27[200D\27[K')
|
|
else io.write('\27[1A\27[200D\27[K\27[1A\27[200D\27[K') end
|
|
end
|
|
|
|
--||__||--||__||--||__||--||__||--||__||--||__||--||__||--||
|
|
------------------------------------------------------------
|
|
-- TCP Requests and processing -----------------------------
|
|
------------------------------------------------------------
|
|
|
|
function request_gopher(host, port, query, gtype)
|
|
local tcp = assert(socket.tcp())
|
|
local response = {}
|
|
tcp:settimeout(4, 't')
|
|
local connection = tcp:connect(host, port);
|
|
local request
|
|
|
|
if connection then
|
|
request = tcp:send(query.."\n");
|
|
end
|
|
|
|
if request then
|
|
if gtype ~= 'h' and download_types[gtype] then
|
|
local s = tcp:receive('*a')
|
|
response = s
|
|
else
|
|
while true do
|
|
local s, status, partial = tcp:receive()
|
|
if status == "closed" then break end
|
|
if s ~= '.' then
|
|
table.insert(response, (s or partial))
|
|
end
|
|
end
|
|
end
|
|
elseif connection then
|
|
response[1] = 'iConnection error. Bad request.'
|
|
tcp:close()
|
|
else
|
|
response[1] = 'iConnection error. Bad host.'
|
|
end
|
|
tcp:close()
|
|
return response
|
|
end
|
|
|
|
|
|
function parse_url(u) -- takes URL string
|
|
if not string.find(u, '://', 1, true) then
|
|
u = 'gopher://'..u
|
|
end
|
|
|
|
local default = {port=70, scheme='gopher', path=''}
|
|
local parsed = urlparser.parse(u, default)
|
|
|
|
if string.find(parsed.path, '^/[%d%a]') then
|
|
parsed.gophertype = string.sub(parsed.path, 2, 2)
|
|
parsed.path = string.sub(parsed.path, 3)
|
|
elseif string.sub(parsed.path, -1) == '/' or parsed.path == '' then
|
|
parsed.gophertype = '1'
|
|
else
|
|
parsed.gophertype = '0'
|
|
end
|
|
|
|
parsed.full = parsed.scheme .. '://' .. parsed.host .. ':' .. parsed.port .. '/' .. parsed.gophertype .. parsed.path
|
|
return parsed
|
|
end
|
|
|
|
|
|
function go_to_url(u, add2history, noprint)
|
|
local url = parse_url(u)
|
|
if url.scheme == 'http' then
|
|
print('Attempting to open http link in "lynx"...')
|
|
os.execute(string.format('lynx %s', url.scheme .. '://' .. url.authority .. url.path))
|
|
print('Operation complete')
|
|
return true
|
|
end
|
|
local t = session.tabs[session.current_tab]
|
|
if not url.host or url.scheme ~= 'gopher' then
|
|
print(separator)
|
|
print('Invalid url')
|
|
elseif table.has(valid_types, url.gophertype) or url.gophertype == 'h' then
|
|
if url.query ~= '' and url.query then url.path = url.path .. '?' .. url.query end
|
|
t.filedata = request_gopher(url.host, url.port, url.path, url.gophertype)
|
|
print(separator)
|
|
local res = handle_response(t.filedata, url.gophertype)
|
|
if noprint then
|
|
return res
|
|
else
|
|
print(res)
|
|
end
|
|
local o = url.scheme .. '://' .. url.host .. ':' .. url.port .. '/' .. url.gophertype .. url.path
|
|
print('\n\n')
|
|
print(o)
|
|
if add2history then
|
|
table.truncate(t.history.items, t.history.loc + 1)
|
|
table.insert(t.history.items, o)
|
|
t.history.loc = t.history.loc + 1
|
|
end
|
|
elseif download_types[url.gophertype] then
|
|
download_file(url)
|
|
end
|
|
end
|
|
|
|
|
|
function download_file(u)
|
|
print(separator)
|
|
local save_path = assert(os.getenv('HOME'))
|
|
local fn = nil
|
|
io.write('Save file as: ')
|
|
fn = io.read()
|
|
if not fn or fn == '' then return print('Cancelled') end
|
|
io.write(string.format('Saving as %q, is this correct? ', fn))
|
|
local r = string.lower(io.read())
|
|
if r ~= 'y' and r ~= 'yes' then return false end
|
|
print(string.format('Downloading %q...', fn))
|
|
local data = request_gopher(u.host, u.port, u.path, u.gophertype)
|
|
if not data then return false end
|
|
local f = io.open(save_path .. '/' .. fn, 'wb')
|
|
f:write(data)
|
|
f:close()
|
|
print(string.format('%q saved to %s/%s', fn, save_path, fn))
|
|
end
|
|
|
|
|
|
function handle_response(res_table, gtype)
|
|
local out = ''
|
|
if gtype == '0' or gtype == 0 or gtype == 'h' then
|
|
out = table.concat(res_table, '\n')
|
|
else
|
|
session.tabs[session.current_tab].current_links = {}
|
|
for i, v in ipairs(res_table) do
|
|
out = out .. (display_gophermap_row(v) or '').. '\n'
|
|
end
|
|
end
|
|
|
|
return out
|
|
end
|
|
|
|
|
|
function display_gophermap_row(row)
|
|
local t = session.tabs[session.current_tab]
|
|
local val = string.split(row, '\t')
|
|
local gophertype = string.sub(val[1], 1, 1)
|
|
local text = string.sub(val[1], 2)
|
|
local spacer = ' '
|
|
if gophertype == 'i' then
|
|
return spacer .. (text or '')
|
|
elseif table.has(valid_types, gophertype) and gophertpe ~= '7' then
|
|
local leader = gophertype == '0' and 'TXT' or 'MAP'
|
|
local url = string.format('%s:%s/%s%s', val[3] or '', val[4] or '', gophertype, val[2] or '/')
|
|
table.insert(t.current_links, url)
|
|
local linknum = string.format('(%d)', #t.current_links)
|
|
return string.format(' %s %5s %s', leader, linknum, text) or ''
|
|
else
|
|
local leader = download_types[gophertype]
|
|
if not leader then return '' end
|
|
if gophertype == 'h' and string.sub(val[2],1,3) == 'URL' then
|
|
table.insert(t.current_links, string.sub(val[2], 5))
|
|
else
|
|
local url = string.format('%s:%s/%s%s', val[3] or '', val[4] or '', gophertype, val[2] or '/')
|
|
table.insert(t.current_links, url)
|
|
end
|
|
local linknum = string.format('(%d)', #t.current_links)
|
|
return string.format(' %s %5s %s', leader, linknum, text) or ''
|
|
end
|
|
end
|
|
|
|
|
|
function getch()
|
|
-- taken from: http://lua.2524044.n2.nabble.com/How-to-get-one-keystroke-without-hitting-Enter-td5858614.html
|
|
os.execute("stty cbreak </dev/tty >/dev/tty 2>&1")
|
|
local key = io.read(1)
|
|
if string.byte(key) == 27 then io.read(2) end
|
|
|
|
os.execute("stty -cbreak </dev/tty >/dev/tty 2>&1");
|
|
print('\27[1D ')
|
|
key = string.byte(key) == string.byte('\n') and 'enter' or key
|
|
|
|
return string.lower(key);
|
|
end
|
|
|
|
|
|
|
|
|
|
--||__||--||__||--||__||--||__||--||__||--||__||--||__||--||
|
|
------------------------------------------------------------
|
|
-- Initialize and run application --------------------------
|
|
------------------------------------------------------------
|
|
|
|
function init()
|
|
if arg[1] == '-h' or arg[1] == '--help' then
|
|
print_help()
|
|
os.exit(0)
|
|
end
|
|
new_tab()
|
|
if file_exists(save_file_location) then
|
|
dofile(save_file_location)
|
|
end
|
|
mainloop()
|
|
end
|
|
|
|
|
|
function mainloop()
|
|
local th = session.tabs[session.current_tab].history
|
|
print('\n')
|
|
while true do
|
|
local bar = string.format('\27[7m o--[ S t u b b ]--> (H)elp, (Q)uit TAB:%d/%d HST: %d/%d \27[0m', session.current_tab, #session.tabs,th.loc, #th.items)
|
|
print(bar)
|
|
local key = getch()
|
|
io.write(string.char(27) .. "[100D")
|
|
io.write(string.char(27) .. "[1A")
|
|
io.write(string.char(27) .. "[K")
|
|
if key == 'q' then save_favorites(); os.exit(0)
|
|
elseif key == 'h' then print_help()
|
|
elseif key == 's' then search()
|
|
elseif key == 'b' then back()
|
|
elseif key == 'f' then forward()
|
|
elseif key == 'u' then update_favorite()
|
|
elseif key == 'r' then refresh()
|
|
elseif key == 'p' then pipe()
|
|
elseif key == 'a' then add()
|
|
elseif key == 'l' then list()
|
|
elseif key == 'd' then delete()
|
|
elseif key == 'g' then go_to()
|
|
elseif key == 'c' then clean_screen()
|
|
elseif key == 'enter' then io.write('\27[2A\27[200D\27[K')
|
|
else print('\27[2A\27[200D\27[K') end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
--||__||--||__||--||__||--||__||--||__||--||__||--||__||--||
|
|
------------------------------------------------------------
|
|
-- Top Level Menu Options ----------------------------------
|
|
------------------------------------------------------------
|
|
|
|
function save_favorites()
|
|
local file = io.open(save_file_location, 'w+')
|
|
file:write("session.favorites = {\n")
|
|
for _,v in ipairs(session.favorites) do
|
|
file:write(string.format(" {[%q] = %q, [%q] = %q},\n", 'display', v.display, 'link', v.link))
|
|
end
|
|
file:write("}\n")
|
|
file:close()
|
|
end
|
|
|
|
|
|
function print_help()
|
|
print(separator)
|
|
print('\nStubb ' .. version)
|
|
local helpstring = [[
|
|
|
|
Stubb is a text based gopher client gophertypes 0 (text) and 1 (gopher map) are
|
|
rendered to the screen, all other types are downloaded to a user's home folder.
|
|
The application is named for the character Stubb from Moby Dick known for his
|
|
imaginative patter and good humor.
|
|
|
|
The command bar lists the current tab number, out of how many total tabs. It also
|
|
lists the current place in history (for the tab you are currently on).
|
|
|
|
Command map:
|
|
|
|
<Navigation>
|
|
(B)ack
|
|
(F)orward
|
|
(G)o to url
|
|
- (B)ookmark
|
|
- (L)ink
|
|
- (T)ab
|
|
- (U)rl
|
|
(R)eload current address
|
|
(P)ipe to
|
|
- (R)eader
|
|
- (C)urrent
|
|
- (L)ink
|
|
- (T)ab
|
|
- (C)urrent
|
|
- (L)ink
|
|
- (D)isk
|
|
- (C)urrent
|
|
- (L)ink
|
|
(S)earch
|
|
|
|
<Bookmarks/Tabs>
|
|
(A)dd
|
|
- (B)ookmark
|
|
- (T)ab
|
|
(D)elete
|
|
- (B)ookmark
|
|
- (T)ab
|
|
(L)ist
|
|
- (B)ookmarks
|
|
- (T)abs
|
|
(U)pdate bookmark title
|
|
|
|
<System>
|
|
(C)lean the screen
|
|
(H)elp
|
|
(Q)uit Stubb
|
|
|
|
For more information, source code, or to get a copy:
|
|
|
|
http://tildegit.org/sloum/stubb
|
|
|
|
Stubb is free, as in freedom, software. Modify it as you see fit, give it to
|
|
whoever you want. It would be great if you referenced the original or cross
|
|
linked, but it is not required.
|
|
|
|
]]
|
|
print(helpstring)
|
|
end
|
|
|
|
|
|
function search()
|
|
io.write('Enter your search terms: ')
|
|
local st = io.read()
|
|
if not st or st == '' then return print('Invalid search entry') end
|
|
if url ~= '!' and url ~= '' then
|
|
go_to_url(search_provider .. '\t' .. st, true)
|
|
else
|
|
print('Cancelled')
|
|
end
|
|
end
|
|
|
|
|
|
function back()
|
|
local th = session.tabs[session.current_tab].history
|
|
if th.loc > 1 and #th.items > 1 then
|
|
th.loc = th.loc - 1
|
|
go_to_url(th.items[th.loc])
|
|
else invalid_key('other') end
|
|
end
|
|
|
|
|
|
function forward()
|
|
local th = session.tabs[session.current_tab].history
|
|
if th.loc < #th.items then
|
|
th.loc = th.loc + 1
|
|
go_to_url(th.items[th.loc])
|
|
else
|
|
invalid_key('other')
|
|
end
|
|
end
|
|
|
|
|
|
function update_favorite()
|
|
local title
|
|
io.write('Enter the bookmark id (! to cancel): ')
|
|
local favid = io.read()
|
|
if favid == '!' then return print('Cancelled') end
|
|
local id = tonumber(favid) or tonumber(string.sub(favid,2))
|
|
if id then
|
|
local item = session.favorites[id]
|
|
if item then
|
|
print(string.format('Old title: %s', item.display))
|
|
while not title or title == '' do
|
|
io.write('Enter new title (! to cancel): ')
|
|
title = io.read()
|
|
if title == '!' then return print('Cancelled') end
|
|
end
|
|
item.display = title
|
|
print('Bookmark has been updated')
|
|
save_favorites()
|
|
else
|
|
print('Invalid bookmark id')
|
|
end
|
|
else
|
|
print('Invalid bookmark id')
|
|
end
|
|
end
|
|
|
|
|
|
function refresh()
|
|
local th = session.tabs[session.current_tab].history
|
|
if th.loc > 0 then
|
|
go_to_url(th.items[th.loc])
|
|
end
|
|
end
|
|
|
|
|
|
function pipe()
|
|
io.write('Pipe to: (D)isk, (R)eader, New (T)ab')
|
|
local subkey = getch()
|
|
if subkey == 'd' then save_file()
|
|
elseif subkey == 't' then open_in_tab()
|
|
elseif subkey == 'r' then open_in_less()
|
|
else invalid_key(subkey) end
|
|
end
|
|
|
|
|
|
function add()
|
|
io.write('Add: (B)ookmark, (T)ab')
|
|
local subkey = getch()
|
|
if subkey == 'b' then add_favorite()
|
|
elseif subkey == 't' then new_tab()
|
|
else invalid_key(subkey) end
|
|
end
|
|
|
|
|
|
function list()
|
|
io.write('List: (B)ookmarks, (T)abs')
|
|
local subkey = getch()
|
|
if subkey == 'b' then show_favorites()
|
|
elseif subkey == 't' then show_tabs()
|
|
else invalid_key(subkey) end
|
|
end
|
|
|
|
|
|
function delete()
|
|
io.write('Delete: (B)ookmark, (T)ab')
|
|
local subkey = getch()
|
|
if subkey == 'b' then remove_favorite()
|
|
elseif subkey == 't' then remove_tab()
|
|
else invalid_key(subkey) end
|
|
end
|
|
|
|
|
|
function go_to()
|
|
io.write('Go to: (B)ookmark, (L)ink, (T)ab, (U)rl')
|
|
local subkey = getch()
|
|
if subkey == 'b' then
|
|
go_to_link(true)
|
|
elseif subkey == 'l' then
|
|
go_to_link()
|
|
elseif subkey == 't' then
|
|
io.write('Enter tab id (! to cancel): ')
|
|
local t = io.read()
|
|
local tabid = tonumber(t)
|
|
if t ~= '!' and tabid and session.tabs[tabid] then
|
|
session.current_tab = tabid
|
|
refresh()
|
|
else
|
|
local x = tabid and print('Invalid tab id') or print('Cancelled')
|
|
end
|
|
elseif subkey == 'u' then
|
|
io.write('Enter gopher url (! to cancel): ')
|
|
local url = io.read()
|
|
if url ~= '!' and url ~= '' then
|
|
go_to_url(url, true)
|
|
else
|
|
print('Cancelled')
|
|
end
|
|
else invalid_key(subkey) end
|
|
end
|
|
|
|
|
|
function clean_screen()
|
|
os.execute('clear')
|
|
end
|
|
|
|
|
|
|
|
--||__||--||__||--||__||--||__||--||__||--||__||--||__||--||
|
|
------------------------------------------------------------
|
|
-- Secondary actions ---------------------------------------
|
|
------------------------------------------------------------
|
|
|
|
function go_to_link(favorite)
|
|
local t = session.tabs[session.current_tab]
|
|
local linkid
|
|
repeat
|
|
io.write('Enter link id (! to cancel): ')
|
|
local inp = io.read()
|
|
if inp == '!' then return print('Cancelled') end
|
|
linkid = tonumber(inp)
|
|
if not linkid and string.sub(inp, 1, 1) == 'f' then
|
|
linkid = tonumber(string.sub(inp, 2))
|
|
end
|
|
until linkid
|
|
local linkurl
|
|
if favorite then
|
|
selection = session.favorites[linkid]
|
|
if selection then
|
|
linkurl = session.favorites[linkid].link
|
|
else
|
|
return print('Invalid bookmark id')
|
|
end
|
|
else
|
|
linkurl = t.current_links[linkid]
|
|
end
|
|
if linkurl then
|
|
table.truncate(t.history.items, t.history.loc + 1)
|
|
go_to_url(linkurl, true)
|
|
else
|
|
local out = string.format('Link id %d does not exist...', linkid)
|
|
print_gopher_error(out)
|
|
end
|
|
end
|
|
|
|
|
|
function open_in_tab()
|
|
local t = session.tabs[session.current_tab]
|
|
local sub_menu = 'Pipe what: (C)urrent, (L)ink'
|
|
local favorite
|
|
io.write('Pipe what: (C)urrent, (L)ink')
|
|
local subkey = getch()
|
|
if subkey == 'c' then
|
|
local target_url = t.history.items[t.history.loc]
|
|
if not target_url then return print('No current page. Cancelled.') end
|
|
new_tab()
|
|
go_to_url(target_url, true)
|
|
elseif subkey == 'l' then
|
|
local linkid
|
|
repeat
|
|
io.write('Enter link id (! to cancel): ')
|
|
local inp = io.read()
|
|
if inp == '!' then return print('Cancelled') end
|
|
linkid = tonumber(inp)
|
|
if not linkid and string.sub(inp, 1, 1) == 'f' then
|
|
linkid = tonumber(string.sub(inp, 2))
|
|
favorite = true
|
|
end
|
|
until linkid
|
|
local linkurl
|
|
if favorite then
|
|
selection = session.favorites[linkid]
|
|
if selection then
|
|
linkurl = session.favorites[linkid].link
|
|
else
|
|
return print('Invalid bookmark id')
|
|
end
|
|
else
|
|
linkurl = t.current_links[linkid]
|
|
end
|
|
if linkurl then
|
|
new_tab()
|
|
go_to_url(linkurl, true)
|
|
else
|
|
local out = string.format('Link id %d does not exist...', linkid)
|
|
print_gopher_error(out)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function open_in_less()
|
|
local th = session.tabs[session.current_tab].history
|
|
local str
|
|
if th.loc > 0 then
|
|
str = go_to_url(th.items[th.loc], false, true)
|
|
end
|
|
|
|
if str then
|
|
local less = io.popen(os.getenv('PAGER') or'less', 'w')
|
|
less:write(str)
|
|
less:close()
|
|
else
|
|
print('No current URL to open in less')
|
|
end
|
|
end
|
|
|
|
|
|
function add_favorite()
|
|
local th = session.tabs[session.current_tab].history
|
|
if #th.items == 0 then return false end
|
|
local title
|
|
repeat
|
|
io.write('Enter title for bookmark (! to cancel): ')
|
|
title = io.read()
|
|
until title
|
|
if title == '!' then return print('Cancelled') end
|
|
table.insert(session.favorites,{display = title, link = th.items[th.loc]})
|
|
print('Bookmark added')
|
|
save_favorites()
|
|
end
|
|
|
|
|
|
function remove_favorite()
|
|
io.write('Enter the bookmark id to remove (! to cancel): ')
|
|
local favid = io.read()
|
|
if favid == '!' then return print('Cancelled') end
|
|
local id = tonumber(favid) or tonumber(string.sub(favid,2))
|
|
if id then
|
|
local item = session.favorites[id]
|
|
if item then
|
|
table.remove(session.favorites, id)
|
|
print('Bookmark has been removed')
|
|
save_favorites()
|
|
else
|
|
print('Invalid bookmark id')
|
|
end
|
|
else
|
|
print('Invalid bookmark id')
|
|
end
|
|
end
|
|
|
|
|
|
function remove_tab()
|
|
io.write('Enter the tab id to remove (! to cancel): ')
|
|
local tabid = io.read()
|
|
if tabid == '!' then return print('Cancelled') end
|
|
local id = tonumber(tabid)
|
|
if id then
|
|
local item = session.tabs[id]
|
|
if item then
|
|
table.remove(session.tabs, id)
|
|
if #session.tabs < session.current_tab then
|
|
session.current_tab = #session.tabs
|
|
refresh()
|
|
end
|
|
print('Tab has been removed')
|
|
else
|
|
print('Invalid tab id')
|
|
end
|
|
else
|
|
print('Invalid tab id')
|
|
end
|
|
end
|
|
|
|
|
|
function print_gopher_error(text)
|
|
print(separator)
|
|
print(text)
|
|
end
|
|
|
|
|
|
function save_file()
|
|
io.write('Save to disk: (C)urrent, (L)ink')
|
|
local subkey = getch()
|
|
local out
|
|
local t = session.tabs[session.current_tab]
|
|
if subkey == 'c' then
|
|
local t = session.tabs[session.current_tab]
|
|
out = t.filedata
|
|
if not out then return print('No file available. Save cancelled.') end
|
|
elseif subkey == 'l' then
|
|
io.write('Enter link id (! to cancel): ')
|
|
local linkid = tonumber(io.read())
|
|
if linkid == '!' then return print('Cancelled') end
|
|
local location = t.current_links[linkid]
|
|
if location then
|
|
local parsed = parse_url(location)
|
|
out = request_gopher(parsed.host, parsed.port, parsed.path)
|
|
if not out then return print('No file data available. Save cancelled.') end
|
|
else
|
|
return print('Invalid link id. Save cancelled.')
|
|
end
|
|
else
|
|
return print('Invalid entry')
|
|
end
|
|
local homedir = assert(os.getenv('HOME'), 'Unable to find home directory')
|
|
io.write('Enter the filename to save as (! to cancel): ' .. homedir .. '/')
|
|
local filename = io.read()
|
|
if filename == '!' or filename == '' then
|
|
return print('Cancelled')
|
|
elseif string.match(filename, '^[%.%w_-]+$') then
|
|
print('Saving...')
|
|
local filepath = homedir .. '/' .. filename
|
|
local f = io.open(filepath, 'w+')
|
|
f:write(table.concat(out,'\n'))
|
|
f:close()
|
|
print(string.format('File saved as: %s', filepath))
|
|
else
|
|
print('Invalid filename')
|
|
end
|
|
end
|
|
|
|
|
|
-- function handle_response(res_table, gtype)
|
|
-- local out = ''
|
|
-- if gtype == '0' or gtype == 0 then
|
|
-- out = table.concat(res_table, '\n')
|
|
-- else
|
|
-- session.tabs[session.current_tab].current_links = {}
|
|
-- for i, v in ipairs(res_table) do
|
|
-- out = out .. (display_gophermap_row(v) or '').. '\n'
|
|
-- end
|
|
-- end
|
|
|
|
-- return out
|
|
-- end
|
|
|
|
|
|
function show_favorites()
|
|
print(separator)
|
|
print('\n >---B O O K M A R K S--->\n\n')
|
|
for i, v in ipairs(session.favorites) do
|
|
print(string.format(' (f%d) %s', i, v.display))
|
|
end
|
|
print('\n' .. separator)
|
|
end
|
|
|
|
|
|
function show_tabs()
|
|
print(separator)
|
|
print('\n [ T A B S ]\n\n')
|
|
for i, v in ipairs(session.tabs) do
|
|
local u = v.history.items[v.history.loc] or 'Empty tab'
|
|
local t = session.current_tab == i and ' --> ' or ' '
|
|
print(string.format('%s(%d) %s', t, i, u))
|
|
end
|
|
print('\n' .. separator)
|
|
end
|
|
|
|
|
|
function file_exists(name)
|
|
local f=io.open(name,"r")
|
|
if f~=nil then io.close(f) return true else return false end
|
|
end
|
|
|
|
|
|
function new_tab()
|
|
local t = {
|
|
history = {
|
|
loc=0,
|
|
items={}
|
|
},
|
|
current_links = {},
|
|
favorites={}
|
|
}
|
|
|
|
table.insert(session.tabs, t)
|
|
session.current_tab = #session.tabs
|
|
end
|
|
|
|
|
|
|
|
-- run the program
|
|
init()
|