Programming and Scripting :: MyDSL info parser



Quote
As 4.x boots is sets the perms for user/group on the mydsl dir so should be accessible.
What if it's a read-only filesystem? You can't set permissions in that case. I think it's possible that a user could use the mydsl boot option to specify an NTFS or CDROM filesystem.

The update function still needs work, particularly in checking the validity of the downloaded file, and because the code is pretty sloppy.
EDIT: minor change, fixed the error message if .mydsl_dir file can't be read.
Code Sample
#!/bin/murgaLua
-- mydsl info browser, 2008 mikshaw
--
-- Changelog (yyyy/mm/dd)
--   2008/01/17: Added date of last update
--               Tweaked the update function (it's still sloppy)
--               Fixed ".mydsl_dir not found" error message
--   2008/01/16: Fixed resizable behavior
--               Uses file in .mydsl_dir
--               Added auto update for first run
--   2008/01/15: Added text search
--   2008/01/14: Fixed display of incorrect duplicate info file
--               Check for trailing spaces in title string
--               Added "Update" function
--               Code and interface tweaks
--   2008/01/13: start of project
--
-- TODO: Clean up the update function

mydsldirfile=io.open("/opt/.mydsl_dir")
if not mydsldirfile then
 print(arg[0]..": Can't open /opt/.mydsl_dir")
 os.exit(1)
end
mydsl_dir=mydsldirfile:read("*l")
mydsldirfile:close()

listfile=mydsl_dir.."/mydslinfo.bz2"
listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/mydslinfo.bz2"

function load_list()
while b_loc:size()>1 do b_loc:remove(0) end
local getdate=io.popen("ls -lh "..listfile.."|awk '{print \"\\n\\n**\\nDatabase updated \"$6\" \"$7\"\\n"..fltk.fl_filename_name(listfile).." \"$5}'")
mdate=getdate:read("*a")
getdate:close()
inputfile = io.popen("bunzip2 -c "..listfile)
data=inputfile:read("*a")
inputfile:close()

if not string.find(data,"%w") then
 info_display_buffer:text("Cannot read database "..listfile.."\nPlease try updating again.")
else
info={}
info_count=0
-- breaks up each info file into a separate string
for s in string.gmatch(data,"(Location:.-\n)Location:") do
 info_count=info_count+1
 info[info_count]={
 location=string.match(s,"Location:%s*(.-)%s*\n"),
 title=string.match(s,"Title:%s*(.-)%s*\n"),
 text=s
 }
 b_loc:add(info[info_count].location)
end

b_loc:value(0)
b_loc:do_callback()
end
end

function updatedb(nofile)
 if not nofile then backup=os.rename(listfile,listfile..".bak") end
 os.execute("aterm +tr -bg white -fg black -geometry 80x4 -title \"Database Update\" -e wget "..listurl.." -O "..listfile)
 load_list()
end

ww=420; wh=360; bh=25; bw=ww/2-5
w=fltk:Fl_Window(ww,wh,"MyDSL Info Browser")

tabs=fltk:Fl_Tabs(0,0,ww,wh)

tab1=fltk:Fl_Group(0,0,ww,wh-bh,"browse")
b_loc=fltk:Fl_Choice(5,5,bw,bh)
b_loc:callback(
function()
 while b_files:size()>1 do b_files:remove(0) end
 b_files:redraw()
   for i=1,info_count do
     if info[i].location==b_loc:text() then
       b_files:add(info[i].title)
     end
   end
 b_files:value(0)
 b_files:do_callback()
end
)

b_files=fltk:Fl_Choice(ww/2,5,bw,bh)
b_files:callback(
function()
 for i=1,info_count do
   if info[i].title == b_files:text() and info[i].location == b_loc:text() then
     info_display_buffer:text(info[i].text)
     info_display_buffer:append(mdate)
     break
   end
 end
end
)

info_display=fltk:Fl_Text_Display(5,10+bh,ww-10,wh-(20+bh*3))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)

install=fltk:Fl_Button(5,wh-bh*2-5,bw,bh,"Install Selected Package")
install:callback(
function()
 os.execute("mydsl-load "..b_files:text().." "..b_loc:text())
end
)

b_updatedb=fltk:Fl_Button(bw+5,wh-bh*2-5,bw,bh,"Update Database")
b_updatedb:callback(updatedb)
fltk:Fl_End()

tab2=fltk:Fl_Group(0,0,ww,wh-bh,"search text")
search_b=fltk:Fl_Return_Button(5,5,80,bh,"search")
search_b:callback(
function()
 if search_text:value() ~= "" then
   results:clear()
   for i=1,info_count do
     if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,true) then
       results:add(info[i].title.."\t\t"..info[i].location)
     end
   end
 end
end
)
search_text=fltk:Fl_Input(85,5,ww-90,bh)
results=fltk:Fl_Hold_Browser(5,10+bh,ww-10,wh-(20+bh*2))
results:textfont(fltk.FL_SCREEN)
results:textsize(12)
results:callback(
function()
-- show selected info file in the browse tab
if results:value() > 0 then
 get_loc=string.match(results:text(results:value()),"\t\t(.*)")
 get_fname=string.match(results:text(results:value()),"(.*)\t\t")
 for i=0,b_loc:size() do
   if get_loc == b_loc:text(i) then
     b_loc:value(i)
     break
   end
 end
 b_loc:do_callback()
 for i=0,b_files:size() do
   if get_fname == b_files:text(i) then
     b_files:value(i)
     break
   end
 end
 b_files:do_callback()
tabs:value(tab1)
end
end
)
fltk:Fl_End()

w:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tab1:resizable(info_display)
tab2:resizable(results)
w:show()
find_file=io.open(listfile)
if find_file then data=find_file:read("*a") else data="" end
if string.find(data,"%w") then
 find_file:close()
 load_list()
else updatedb(1)
end
Fl:run()

Made one small edit. The previous version would concatenate two files if there was no newline between them in the database.
This change assumes that "Location:" is not found anywhere but the beginning of the first line of a given info.

EDIT: I'm noticing that some infos are not being found, even when doing a text search. Before this last change a search for gimp displayed the uci package in testing, but after it didn't. I do realize that the string used in string.gmatch is faulty, but I don't know if that's the only problem.

Code Sample
#!/bin/murgaLua
-- mydsl info browser, 2008 mikshaw
--
-- Changelog (yyyy/mm/dd)
--   2008/01/24: Fixed string.gmatch to break up files even if there is no newline between them
--   2008/01/17: Added date of last update
--               Tweaked the update function (it's still sloppy)
--               Fixed ".mydsl_dir not found" error message
--   2008/01/16: Fixed resizable behavior
--               Uses file in .mydsl_dir
--               Added auto update for first run
--   2008/01/15: Added text search
--   2008/01/14: Fixed display of incorrect duplicate info file
--               Check for trailing spaces in title string
--               Added "Update" function
--               Code and interface tweaks
--   2008/01/13: start of project
--
-- TODO: Clean up the update function

mydsldirfile=io.open("/opt/.mydsl_dir")
if not mydsldirfile then
 print(arg[0]..": Can't open /opt/.mydsl_dir")
 os.exit(1)
end
mydsl_dir=mydsldirfile:read("*l")
mydsldirfile:close()

listfile=mydsl_dir.."/mydslinfo.bz2"
listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/mydslinfo.bz2"

function load_list()
while b_loc:size()>1 do b_loc:remove(0) end
local getdate=io.popen("ls -lh "..listfile.."|awk '{print \"\\n\\n**\\nDatabase updated \"$6\" \"$7\"\\n"..fltk.fl_filename_name(listfile).." \"$5}'")
mdate=getdate:read("*a")
getdate:close()
inputfile = io.popen("bunzip2 -c "..listfile)
data=inputfile:read("*a")
inputfile:close()

if not string.find(data,"%w") then
 info_display_buffer:text("Cannot read database "..listfile.."\nPlease try updating again.")
else
info={}
info_count=0
-- breaks up each info file into a separate string
-- assumes "Location:" is found only once per info file
for s in string.gmatch(data,"(Location:.-)Location:") do
 info_count=info_count+1
 info[info_count]={
 location=string.match(s,"Location:%s*(.-)%s*\n"),
 title=string.match(s,"Title:%s*(.-)%s*\n"),
 text=s
 }
 b_loc:add(info[info_count].location)
end

b_loc:value(0)
b_loc:do_callback()
end
end

function updatedb(nofile)
 if not nofile then backup=os.rename(listfile,listfile..".bak") end
 os.execute("aterm +tr -bg white -fg black -geometry 80x4 -title \"Database Update\" -e wget "..listurl.." -O "..listfile)
 load_list()
end

ww=420; wh=360; bh=25; bw=ww/2-5
w=fltk:Fl_Window(ww,wh,"MyDSL Info Browser")

tabs=fltk:Fl_Tabs(0,0,ww,wh)

tab1=fltk:Fl_Group(0,0,ww,wh-bh,"browse")
b_loc=fltk:Fl_Choice(5,5,bw,bh)
b_loc:callback(
function()
 while b_files:size()>1 do b_files:remove(0) end
 b_files:redraw()
   for i=1,info_count do
     if info[i].location==b_loc:text() then
       b_files:add(info[i].title)
     end
   end
 b_files:value(0)
 b_files:do_callback()
end
)

b_files=fltk:Fl_Choice(ww/2,5,bw,bh)
b_files:callback(
function()
 for i=1,info_count do
   if info[i].title == b_files:text() and info[i].location == b_loc:text() then
     info_display_buffer:text(info[i].text)
     info_display_buffer:append(mdate)
     break
   end
 end
end
)

info_display=fltk:Fl_Text_Display(5,10+bh,ww-10,wh-(20+bh*3))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)

install=fltk:Fl_Button(5,wh-bh*2-5,bw,bh,"Install Selected Package")
install:callback(
function()
 os.execute("mydsl-load "..b_files:text().." "..b_loc:text())
end
)

b_updatedb=fltk:Fl_Button(bw+5,wh-bh*2-5,bw,bh,"Update Database")
b_updatedb:callback(updatedb)
fltk:Fl_End()

tab2=fltk:Fl_Group(0,0,ww,wh-bh,"search text")
search_b=fltk:Fl_Return_Button(5,5,80,bh,"search")
search_b:callback(
function()
 if search_text:value() ~= "" then
   results:clear()
   for i=1,info_count do
     if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,true) then
       results:add(info[i].title.."\t\t"..info[i].location)
     end
   end
 end
end
)
search_text=fltk:Fl_Input(85,5,ww-90,bh)
results=fltk:Fl_Hold_Browser(5,10+bh,ww-10,wh-(20+bh*2))
results:textfont(fltk.FL_SCREEN)
results:textsize(12)
results:callback(
function()
-- show selected info file in the browse tab
if results:value() > 0 then
 get_loc=string.match(results:text(results:value()),"\t\t(.*)")
 get_fname=string.match(results:text(results:value()),"(.*)\t\t")
 for i=0,b_loc:size() do
   if get_loc == b_loc:text(i) then
     b_loc:value(i)
     break
   end
 end
 b_loc:do_callback()
 for i=0,b_files:size() do
   if get_fname == b_files:text(i) then
     b_files:value(i)
     break
   end
 end
 b_files:do_callback()
tabs:value(tab1)
end
end
)
fltk:Fl_End()

w:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tab1:resizable(info_display)
tab2:resizable(results)
w:show()
find_file=io.open(listfile)
if find_file then data=find_file:read("*a") else data="" end
if string.find(data,"%w") then
 find_file:close()
 load_list()
else updatedb(1)
end
Fl:run()

Since Robert improved the database file to include a space between each info, I reverted the last change to be more reliable.  Also did a couple of bug fixes and made the whole thing keyboard friendly.

It's still not ideal. I need to work out a better way to handle the db update. As it is, a current database file can be clobbered by a failed update, even with the half-assed backup system in place, such as if the update fails twice. I probably should have it download to a temp file first, check the integrity of the file, and then overwrite the prior file, instead of making a backup copy.

Code Sample
#!/home/dsl/bin/murgaLua-0.6.4
--[[
    MyDSL info browser, 2008 mikshaw

    Changelog (yyyy/mm/dd)
      2008/03/14: Undid gmatch change due to improvements to the database file
                  Improved keyboard control
                  Use Fl_Menu_Button for menus to add to keyboard control
                  Fixed crash on search or install with an empty database
      2008/01/24: Fixed string.gmatch to break up files even if there is no newline between them
      2008/01/17: Added date of last update
                  Tweaked the update function (it's still sloppy)
                  Fixed ".mydsl_dir not found" error message
      2008/01/16: Fixed resizable behavior
                  Uses file in .mydsl_dir
                  Added auto update for first run
      2008/01/15: Added text search
      2008/01/14: Fixed display of incorrect duplicate info file
                  Check for trailing spaces in title string
                  Added "Update" function
                  Code and interface tweaks
      2008/01/13: start of project
   
    TODO: Clean up the update function

--]]

mydsldirfile=io.open("/opt/.mydsl_dir")
if not mydsldirfile then
 print(arg[0]..": Can't open /opt/.mydsl_dir")
 os.exit(1)
end
mydsl_dir=mydsldirfile:read("*l")
mydsldirfile:close()

listfile=mydsl_dir.."/mydslinfo.bz2"
listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/mydslinfo.bz2"

function load_list() -- load the database
while b_loc:size()>1 do b_loc:remove(0) end
local getdate=io.popen("ls -lh "..listfile.."|awk '{print \"\\n\\n**\\nDatabase updated \"$6\" \"$7\"\\n"..fltk.fl_filename_name(listfile).." \"$5}'")
mdate=getdate:read("*a")
getdate:close()
inputfile = io.popen("bunzip2 -c "..listfile)
data=inputfile:read("*a")
inputfile:close()
if not string.find(data,"%w") then
 info_display_buffer:text("Cannot read database "..listfile.."\nPlease try updating again.")
else
info={}
info_count=0
-- breaks up each info file into a separate string
for s in string.gmatch(data,"(Location:.-)\nLocation:") do
 info_count=info_count+1
 info[info_count]={
 location=string.match(s,"Location:%s*(.-)%s*\n"),
 title=string.match(s,"Title:%s*(.-)%s*\n"),
 text=s
 }
 b_loc:add(info[info_count].location)
end

b_loc:value(0)
b_loc:do_callback()
end
end

function updatedb(nofile)
 if not nofile then backup=os.rename(listfile,listfile..".bak") end
 os.execute("aterm +tr -bg white -fg black -geometry 80x4 -title \"Database Update\" -e wget "..listurl.." -O "..listfile)
 load_list()
end

function pick_search_result()
if results:value() > 1 then
-- show selected info file in the browse tab
 get_loc=string.match(results:text(results:value()),spacer.."(.*)")
 get_fname=string.match(results:text(results:value()),"(.*)"..spacer)
 results:deselect()
 for i=0,b_loc:size() do
   if get_loc == b_loc:text(i) then
     b_loc:value(i)
     break
   end
 end
 b_loc:do_callback()
 for i=0,b_files:size() do
   if get_fname == b_files:text(i) then
     b_files:value(i)
     break
   end
 end
 b_files:do_callback()
tabs:value(tab1)
end
end

ww=420; wh=360; bh=30; bw=ww/2-5
win=fltk:Fl_Window(ww,wh,"MyDSL Info Browser")

tabs=fltk:Fl_Tabs(0,0,ww,wh)

tab1=fltk:Fl_Group(0,0,ww,wh-bh,"browse")
b_loc=fltk:Fl_Menu_Button(5,5,bw,bh)
b_loc:align(20)
b_loc:callback(
function()
 b_loc:label("&Location:  "..b_loc:text())
 while b_files:size()>1 do b_files:remove(0) end
 b_files:redraw()
   for i=1,info_count do
     if info[i].location==b_loc:text() then
       b_files:add(info[i].title)
     end
   end
 b_files:value(0)
 b_files:do_callback()
end
)

b_files=fltk:Fl_Menu_Button(ww/2,5,bw,bh)
b_files:align(20)
b_files:callback(
function()
 b_files:label("&Filename:  "..b_files:text())
 for i=1,info_count do
   if info[i].title == b_files:text() and info[i].location == b_loc:text() then
     info_display_buffer:text(info[i].text)
     info_display_buffer:append(mdate)
     break
   end
 end
end
)

info_display=fltk:Fl_Text_Display(5,10+bh,ww-10,wh-(20+bh*3))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)

install=fltk:Fl_Button(5,wh-bh*2-5,bw,bh,"&Install Selected Package")
install:callback(
function()
if b_files:text() and b_loc:text() then
 os.execute("mydsl-load "..b_files:text().." "..b_loc:text())
end
end
)

b_updatedb=fltk:Fl_Button(bw+5,wh-bh*2-5,bw,bh,"&Update Database")
b_updatedb:callback(updatedb)
fltk:Fl_End() --end tab1

tab2=fltk:Fl_Group(0,0,ww,wh-bh,"search text")
search_text=fltk:Fl_Input(70,5,ww-75,bh,"search text: ")
search_text:when(fltk.FL_WHEN_ENTER_KEY)
search_text:callback(
function()
 if search_text:value() ~= "" and info_count and info_count>0 then
   results:clear()
   results:add("@b@B49PACKAGE NAME"..spacer.."@b@B49LOCATION")
   for i=1,info_count do
     if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,true) then
       results:add(info[i].title..spacer..info[i].location)
     end
   end
 end
end
)

results=fltk:Fl_Hold_Browser(5,10+bh,ww-10,wh-(20+bh*2))
results:textfont(fltk.FL_SCREEN)
results:textsize(12)
-- column widths available in 0.6.4+
if Fl_Browser.column_widths then
 results:column_widths({results:w()/2,0})
 spacer="\t"
else spacer="\t\t" -- force some extra space for older versions
end
results:callback(
-- react to click only
function()
if results:value() > 1 and Fl:event()==fltk.FL_RELEASE then
 pick_search_result() end
end
)
-- offscreen results_callback button for Enter key
results_cb=fltk:Fl_Return_Button(ww,wh,bh,bh)
results_cb:callback(pick_search_result)
fltk:Fl_End() --end tab2
fltk:Fl_End() --end tabs group

-- offscreen button gives focus to text input rather than just the tab
toggle_search=fltk:Fl_Button(ww,wh,bh,bh,"&s")
toggle_search:callback(
function()
 tabs:value(tab2)
 Fl:focus(search_text)
end
)
toggle_browse=fltk:Fl_Button(ww,wh,bh,bh,"&b")
toggle_browse:callback(function() tabs:value(tab1) end)

win:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tab1:resizable(info_display)
tab2:resizable(results)
win:show()
find_file=io.open(listfile)
if find_file then data=find_file:read("*a") else data="" end
if string.find(data,"%w") then
 find_file:close()
 load_list()
else updatedb(1)
end
Fl:run()

OK, I think the update function works properly now. No backup file being shuffled around, it's just overwrting the old database file only if the new one is good.
Code Sample
#!/home/dsl/bin/murgaLua-0.6.4
--[[
    MyDSL info browser, 2008 mikshaw

    Changelog (yyyy/mm/dd)
      2008/03/15: Improved the update function
      2008/03/14: Undid gmatch change due to improvements to the database file
                  Improved keyboard control
                  Use Fl_Menu_Button for menus to add to keyboard control
                  Fixed crash on search or install with an empty database
      2008/01/24: Fixed string.gmatch to break up files even if there is no newline between them
      2008/01/17: Added date of last update
                  Tweaked the update function (it's still sloppy)
                  Fixed ".mydsl_dir not found" error message
      2008/01/16: Fixed resizable behavior
                  Uses file in .mydsl_dir
                  Added auto update for first run
      2008/01/15: Added text search
      2008/01/14: Fixed display of incorrect duplicate info file
                  Check for trailing spaces in title string
                  Added "Update" function
                  Code and interface tweaks
      2008/01/13: start of project
   
--]]

mydsldirfile=io.open("/opt/.mydsl_dir")
if not mydsldirfile then
 print(arg[0]..": Can't open /opt/.mydsl_dir")
 os.exit(1)
end
mydsl_dir=mydsldirfile:read("*l")
mydsldirfile:close()

listfile=mydsl_dir.."/mydslinfo.bz2"
listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/mydslinfo.bz2"

function load_list() -- load the database
while b_loc:size()>1 do b_loc:remove(0) end
local getdate=io.popen("ls -lh "..listfile.."|awk '{print \"\\n\\n**\\nDatabase updated \"$6\" \"$7\"\\n"..fltk.fl_filename_name(listfile).." \"$5}'")
mdate=getdate:read("*a")
getdate:close()
inputfile = io.popen("bunzip2 -c "..listfile)
data=inputfile:read("*a")
inputfile:close()
if not string.find(data,"%w") then
 info_display_buffer:text("Cannot read database "..listfile.."\nPlease try updating again.")
else
info={}
info_count=0
-- breaks up each info file into a separate string
for s in string.gmatch(data,"(Location:.-)\nLocation:") do
 info_count=info_count+1
 info[info_count]={
 location=string.match(s,"Location:%s*(.-)%s*\n"),
 title=string.match(s,"Title:%s*(.-)%s*\n"),
 text=s
 }
 b_loc:add(info[info_count].location)
end

b_loc:value(0)
b_loc:do_callback()
end
end

function updatedb()
-- download to temp file, check file integrity, and only then replace the old file
 tempfile=os.tmpname()
 os.execute("aterm +tr -bg white -fg black -geometry 80x4 -title \"Database Update\" -e wget "..listurl.." -O "..tempfile)
 if os.execute("bzip2 -t "..tempfile) == 0 then
   os.rename(tempfile,listfile)
   load_list()
 else fltk.fl_alert("Database check failed.\nThis may be the result of an incomplete download\nor gremlins in your internets.")
 os.remove(tempfile)
 end
end

function pick_search_result()
if results:value() > 1 then
-- show selected info file in the browse tab
 get_loc=string.match(results:text(results:value()),spacer.."(.*)")
 get_fname=string.match(results:text(results:value()),"(.*)"..spacer)
 results:deselect()
 for i=0,b_loc:size() do
   if get_loc == b_loc:text(i) then
     b_loc:value(i)
     break
   end
 end
 b_loc:do_callback()
 for i=0,b_files:size() do
   if get_fname == b_files:text(i) then
     b_files:value(i)
     break
   end
 end
 b_files:do_callback()
tabs:value(tab1)
end
end

ww=420; wh=360; bh=30; bw=ww/2-5
win=fltk:Fl_Window(ww,wh,"MyDSL Info Browser")

tabs=fltk:Fl_Tabs(0,0,ww,wh)

tab1=fltk:Fl_Group(0,0,ww,wh-bh,"browse")
b_loc=fltk:Fl_Menu_Button(5,5,bw,bh)
b_loc:align(20)
b_loc:callback(
function()
 b_loc:label("&Location:  "..b_loc:text())
 while b_files:size()>1 do b_files:remove(0) end
 b_files:redraw()
   for i=1,info_count do
     if info[i].location==b_loc:text() then
       b_files:add(info[i].title)
     end
   end
 b_files:value(0)
 b_files:do_callback()
end
)

b_files=fltk:Fl_Menu_Button(ww/2,5,bw,bh)
b_files:align(20)
b_files:callback(
function()
 b_files:label("&Filename:  "..b_files:text())
 for i=1,info_count do
   if info[i].title == b_files:text() and info[i].location == b_loc:text() then
     info_display_buffer:text(info[i].text)
     info_display_buffer:append(mdate)
     break
   end
 end
end
)

info_display=fltk:Fl_Text_Display(5,10+bh,ww-10,wh-(20+bh*3))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)

install=fltk:Fl_Button(5,wh-bh*2-5,bw,bh,"&Install Selected Package")
install:callback(
function()
if b_files:text() and b_loc:text() then
 os.execute("mydsl-load "..b_files:text().." "..b_loc:text())
end
end
)

b_updatedb=fltk:Fl_Button(bw+5,wh-bh*2-5,bw,bh,"&Update Database")
b_updatedb:callback(updatedb)
fltk:Fl_End() --end tab1

tab2=fltk:Fl_Group(0,0,ww,wh-bh,"search text")
search_text=fltk:Fl_Input(70,5,ww-75,bh,"search text: ")
search_text:when(fltk.FL_WHEN_ENTER_KEY)
search_text:callback(
function()
 if search_text:value() ~= "" and info_count and info_count>0 then
   results:clear()
   if Fl_Browser.column_widths then
     results:add("@b@B49PACKAGE NAME"..spacer.."@b@B49LOCATION")
   else
     results:add("@b@B49PACKAGE NAME"..spacer.."LOCATION")
   end
   for i=1,info_count do
     if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,true) then
       results:add(info[i].title..spacer..info[i].location)
     end
   end
 end
end
)

results=fltk:Fl_Hold_Browser(5,10+bh,ww-10,wh-(20+bh*2))
results:textfont(fltk.FL_SCREEN)
results:textsize(12)
-- column widths available in 0.6.4+
if Fl_Browser.column_widths then
 results:column_widths({results:w()/2,0})
 spacer="\t"
else spacer="\t\t" -- force some extra space for older versions
end
results:callback(
-- react to click only
function()
if results:value() > 1 and Fl:event()==fltk.FL_RELEASE then
 pick_search_result() end
end
)
-- offscreen results_callback button for Enter key
results_cb=fltk:Fl_Return_Button(ww,wh,bh,bh)
results_cb:callback(pick_search_result)
fltk:Fl_End() --end tab2
fltk:Fl_End() --end tabs group

-- offscreen button gives focus to text input rather than just the tab
toggle_search=fltk:Fl_Button(ww,wh,bh,bh,"&s")
toggle_search:callback(
function()
 tabs:value(tab2)
 Fl:focus(search_text)
end
)
toggle_browse=fltk:Fl_Button(ww,wh,bh,bh,"&b")
toggle_browse:callback(function() tabs:value(tab1) end)

win:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tab1:resizable(info_display)
tab2:resizable(results)
win:show()
find_file=io.open(listfile)
if find_file then data=find_file:read("*a") else data="" end
if string.find(data,"%w") then
 find_file:close()
 load_list()
else updatedb()
end
Fl:run()

Next Page...
original here.