MyDSL info parser


Forum: Programming and Scripting
Topic: MyDSL info parser
started by: mikshaw

Posted by mikshaw on Jan. 13 2008,15:39
Quote (roberts @ Jan. 13 2008,01:35)
Also note that I am also now updating a < Master List > of all extensions.
Use your browser to search it, or download it and use Beaver to search it. Or have fun with grep.


Here's a simple gui that can be used to parse this file, currently by filename, package builder, or extension category.

NOTE: This is a quick test, and should not be considered complete or stable.
The list.txt file must be saved locally
The application must be run from a terminal in order to view any output
Text parsing needs work. The "filename" in particular could be handled better...it will accept Lua string patters, but needs further testing to determine whether certain input could be harmful.

Code Sample
listfile="list.txt"

inputfile=io.open(listfile)
if not inputfile then
 print("can't open "..listfile)
 os.exit(1)
end

data=inputfile:read("*a")
inputfile:close()

function parse_info()
if radio_buttons[1]:value()==1 then searchstring="Location:%s*"..inputs[1]:value()
elseif radio_buttons[2]:value()==1 then searchstring="\nExtension.by:%s*"..inputs[2]:value()
elseif radio_buttons[3]:value()==1 then searchstring="\nTitle:%s*"..inputs[3]:value()
else searchstring=nil
end
if searchstring then
for info in string.gmatch(data,"(Location:.-\n)Location:") do
if string.find(info,searchstring) then
print(info.."\n..........\n")
end
end
end
end

w=fltk:Fl_Window(350,200,"MyDSL Info Search")

radio_labels={"category","extension by","filename"}
radio_buttons={};inputs={}
for i=1,table.getn(radio_labels) do
radio_buttons[i]=fltk:Fl_Radio_Round_Button(10,20*i,100,20,radio_labels[i])
inputs[i]=fltk:Fl_Input(120,20*i,200,20)
end

gobutton=fltk:Fl_Button(10,100,100,20,"go")
gobutton:callback(parse_info)

w:show()
Fl:run()

Posted by roberts on Jan. 13 2008,16:18
Hi mikshaw,

I was going to do something here, but I am glad to see that you have started! This could be a replacement to the current box of buttons that I original did.

I was thinking of something along the lines of QBE (Query By Example). Where each field of the info file is displayed as input fields. Then search the list.txt testing each field for corresponding input fields. Display the results in a gui selection list. Then user can scroll results in the GUI and if select Title: field then use its postion -1 to get location. Then easy to feed mydsl-load. Of course, if the use selects something other than title field, display error with help, or iterate backwards to the title field and construct title location to feed mydsl-load.

Finally add a file browser to provide the Load Local feature and we could have a replacment to the existing box-of-buttons currently known as mydsl browser.

If a QBE form is created then it could also be used to create new .info files for those wishing to submit new extensions.

Posted by lucky13 on Jan. 13 2008,16:31
Quote
I was thinking of something along the lines of QBE (Query By Example).

Does murgalua have functional sqlite bindings? I looked at the site but the links to examples and sqlite are broken.

Posted by roberts on Jan. 13 2008,16:58
Yes. murgaLua does. I tried importing this into an sqlite db but was only about 90% successful. The comments are full of odd characters that break things. Using blobs might help, but then not sure of search capabilities. Does 750 items warrant using an sql database? It would be easy moving forward. It is the past 10% of the data that is difficult.
Posted by mikshaw on Jan. 13 2008,17:04
I have no experience with or current knowledge of using databases of any kind, but as far as I understand, sqlite is useable in murgaLua.

I've read the QBE explanation 3 times now, and still don't follow it =o)
But I assume it is directed mainly toward the ultimate goal of obtaining the url for a specific extension, which I suppose would be the main purpose of a tool like this. I was just doing it for the sake of figuring out how to grab the text of a specific info file or files, without a solid plan of actually putting that information to any useful purpose.

I'll have to save this thread for further digestion.

Posted by lucky13 on Jan. 13 2008,17:39
Quote
The comments are full of odd characters that break things. Using blobs might help, but then not sure of search capabilities.... It is the past 10% of the data that is difficult.

If it's 10%, can those characters be changed? Or would you effectively be reformatting the info files altogether?

Quote
Does 750 items warrant using an sql database? It would be easy moving forward.

It would be a lot easier moving forward.

Posted by roberts on Jan. 13 2008,17:49
I think I will take a second look. I have wanted to make an sqlite GUI for DSL. If I could exclude the comments it would be a snap.

The program that I wrote on the server side that generates the html tables is an awk program. Part by position, part by parsing field names. I have been wanting to replace it too. By using sqlite I can drop the awk program for the website tables and have a nice gui for the client side.

Posted by mikshaw on Jan. 13 2008,21:05
I made a variant that might be closer to what you were talking about. It creates two pulldown menus, but I can't say if they might have performance issues on slow machines.

The "Download and Install" button currently just prints the command that would ordinarily be executed. The "Download" button does nothing. I was debating whether or not to have a separate action to simply download the extension to a chosen directory for future use.

Code Sample
#!/bin/murgaLua
-- mydsl info browser, 2008 mikshaw
--
-- Changelog (yyyy/mm/dd)
--   2008/01/14: Fixed display of incorrect duplicate info file
--               Check for trailing spaces in title string
--   2008/01/13: start of project

listfile="/home/dsl/WORK/mydsl_master_list/list.txt"

inputfile=io.open(listfile)
if not inputfile then
 print("can't open "..listfile)
 os.exit(1)
end

data=inputfile:read("*a")
inputfile:close()

w=fltk:Fl_Window(350,400,"MyDSL Info Search")

b_loc=fltk:Fl_Choice(100,10,240,30,"category: ")
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(100,40,240,30,"file: ")
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)
     break
   end
 end
end
)

info_display=fltk:Fl_Text_Display(10,70,330,280)
--info_display:textfont(fltk.FL_COURIER)
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)

download=fltk:Fl_Button(10,350,120,30,"Download")
install=fltk:Fl_Button(130,350,120,30,"Download and Install")
install:callback(
function()
 print("mydsl-load "..b_files:text().." "..b_loc:text())
end
)

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()
w:resizable(info_display)
w:show()
Fl:run()

Posted by mikshaw on Jan. 14 2008,16:38
There seems to be two issues so far that I don't know how to deal with, one is with the list file itself and one I think is in my script.

There are a few infos in the text file that are run together, such as wvdial.dsl and zsh.uci in testing. This may be the result of a missing newline at the end of the file?

Some infos will not display in this tool, such as japanese_fonts.tar.gz in testing. I'm not sure why but it may be related to a mix of spaces and tabs. I thought %s* covered both of these, though.

EDIT: fixed the second issue. It was caused by trailing spaces that were getting included in the title string.

Posted by roberts on Jan. 14 2008,19:49
I think you are likely seeing issues that I have seen in trying to import into sql database.

The info files come from users all over the globle.
Interestingly enough doing a 'file' command on the individual .info files reveals the following:

ASCII text
ASCII English text
ISO-8859 text
ISO-8859 English text
UTF-8 Unicode English text

This mix together with the array of special characters in the comments field, which, by the way, does not even exist for all .info files, has been challenging to say the least.

Not sure how or which murgaLua supports, or even the bash version within DSL.

Posted by mikshaw on Jan. 14 2008,21:22
Another thing that concerns me is the size of the list file. It's now 700kb and will only get larger over time.
This is a pretty large download for a dialup user to have every time the app is loaded.
You might want to consider the possibility of storing the file locally, maybe downloading it automatically if it doesn't already exist.
After that, the user could have the option of either automatically downloading it or updating the file at the user's leisure.


I've implemented a kind of update function, although I'm not sure how reliable it is
Code Sample
#!/bin/murgaLua
-- mydsl info browser, 2008 mikshaw
--
-- Changelog (yyyy/mm/dd)
--   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

listfile="/home/dsl/WORK/mydsl_master_list/list.txt"
listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/list.txt"

function load_list()
inputfile=io.open(listfile)
if not inputfile then
 print("can't open "..listfile)
 os.exit(1)
end

data=inputfile:read("*a")
inputfile:close()

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

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

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)
     break
   end
 end
end
)

info_display=fltk:Fl_Text_Display(5,10+bh,ww-10,wh-(20+bh*2))
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-5,bw,bh,"Install Selected Package")
install:callback(
function()
 os.execute("mydsl-load "..b_files:text().." "..b_loc:text())
end
)

updatedb=fltk:Fl_Button(bw+5,wh-bh-5,bw,bh,"Update Database")
updatedb:callback(
function()
 backup=os.rename(listfile,listfile..".bak")
 if backup then
   wget=os.execute("wget "..listurl.." -O "..listfile)
   if wget==0 then
     while b_loc:size()>1 do b_loc:remove(0) end
     load_list()
   else os.rename(listfile..".bak",listfile)
   end
 end
end
)

load_list()
w:resizable(info_display)
w:show()
Fl:run()

Posted by ^thehatsrule^ on Jan. 14 2008,23:08
Quote
Another thing that concerns me is the size of the list file. It's now 700kb and will only get larger over time.
This is a pretty large download for a dialup user to have every time the app is loaded.
You might want to consider the possibility of storing the file locally, maybe downloading it automatically if it doesn't already exist.
After that, the user could have the option of either automatically downloading it or updating the file at the user's leisure.
I'm pretty sure a gzip'd version could also be uploaded... and since it's all text, the compression ratio should be good enough to just replace the file if there is a requested update.

I was originally using a shell script for utilizing the list.txt, but I'm glad that a lua version is being developed since it will be more streamlined.

Posted by mikshaw on Jan. 14 2008,23:23
Quote
I'm pretty sure a gzip'd version could also be uploaded... and since it's all text, the compression ratio should be good enough to just replace the file if there is a requested update.
I like that idea. A test on the current file compresses it from 704k to 187k

Also edited the previous post. I made some cosmetic changes to the interface for better use of visual space.

Posted by mikshaw on Jan. 15 2008,20:46
Added text search:
EDIT: made text search case insensitive (string.lower on both the match string and the info text contents)
Code Sample
#!/bin/murgaLua
-- mydsl info browser, 2008 mikshaw
--
-- Changelog (yyyy/mm/dd)
--   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

listfile="/home/dsl/WORK/mydsl_master_list/list.txt"
listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/list.txt"

function load_list()
inputfile=io.open(listfile)
if not inputfile then
 print("can't open "..listfile)
 os.exit(1)
end

data=inputfile:read("*a")
inputfile:close()

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

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)
     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
)

updatedb=fltk:Fl_Button(bw+5,wh-bh*2-5,bw,bh,"Update Database")
updatedb:callback(
function()
 backup=os.rename(listfile,listfile..".bak")
 if backup then
   wget=os.execute("wget "..listurl.." -O "..listfile)
   if wget==0 then
     while b_loc:size()>1 do b_loc:remove(0) end
     load_list()
   else os.rename(listfile..".bak",listfile)
   end
 end
end
)
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()

load_list()
--w:resizable(info_display)
--w:resizable(results)
w:show()
Fl:run()

Posted by roberts on Jan. 15 2008,21:59
Looking good, mikshaw.

I have bzip2 compresed the text database list.txt and renamed it to mydslinfo.bz2

Here are my requests:

1. Use the contents of /opt/.mydsl_dir to store the mydslinfo.bz2 database file.
   I am trying to keep all mydsl related items together.

2. Display a count of items in database and date last updated (downloaded).

3. When initially starting, call get (update) database to download as per item 1,
   as I don't want to place an initial mydslinfo.bz2 in the iso.

Posted by mikshaw on Jan. 15 2008,23:23
Those requests are very reasonable.

There are two things I'll need help with:
1) As the file is bzipped, I assume that means reading the file will require bunzipping before being able to read it into lua. This could be done by unzipping the file directly and then reading the unzipped file. Is it possible to pipe the output of bunzip to lua in order to prevent having to create a new file?
2) While testing mydsl-install from the "install" button I noticed that it downloaded the extension to /home/dsl rather than to the directory in /opt/mydsl.dir. Do I need to specify the directory? I assumed mydsl.dir was used by mydsl-load when it downoaded.

I'm also having a really difficult time creating a single resizeable widget. According to the FLTK docs resizable can be very specific, but I can't figure out how to resize just one single widget. I'd like to be able to make the main output fields be resizable, but nothing else =op

Posted by roberts on Jan. 15 2008,23:47
1. change inputfile to:

inputfile = io.popen("bunzip2 -c "..listfile)

2. I will adjust that in mydsl-load. Was presently not enforced, but expected users to be in the mydsl folder. But much better to be enforced.

Posted by mikshaw on Jan. 16 2008,00:59
It's very possible that /opt/mydsl.dir points to a read-only directory. In that case should it fall back to $HOME?
Posted by roberts on Jan. 16 2008,16:04
I am talking about v4.x, it is not likely to be read-only.

However booting 4.x, without specifying the mydsl boot option, results in a default of /tmp/mydsl/ being used and becomes the content of /opt/.mydsl_dir. As 4.x boots is sets the perms for user/group on the mydsl dir so should be accessible.

Even with a custom mydsl.iso containing a mydsl dir, that is read-only, the mydsl dir becomes secondary to a specified mydsl boot option or /tmp/mydsl.



Posted by mikshaw on Jan. 17 2008,00:19
WARNING: This version is half-baked. I need to rework the update function and do a better job of determining whether or not the mydslinfo.bz2 file is usable. I'm still working out how to visually show the user the status of the wget download. Since the current version uses the exit status of wget, that wouldn't work if wget was running in aterm, so I'll probably need to check the file after the download completes as mydsl-load does. But I don't feel like working on it any more today, so I'll post what I have for feedback.

I'm mostly happy, though, because I finally understand how to use the resizable method to specify a nested object to resize (a widget inside a group inside a group).

Code Sample
#!/bin/murgaLua
-- mydsl info browser, 2008 mikshaw
--
-- Changelog (yyyy/mm/dd)
--   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: Fix the update function to not create empty file
--       Let the user know what's happening during update

mydsldirfile=io.open("/opt/.mydsl_dir")
if not mydsldirfile then
 print("can't open "..mydsldirfile)
 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()
inputfile = io.popen("bunzip2 -c "..listfile)
data=inputfile:read("*a")
inputfile:close()

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

function updatedb(nofile)
 if not nofile then backup=os.rename(listfile,listfile..".bak") end
--  if backup then
   wget=os.execute("wget "..listurl.." -O "..listfile)
   if wget==0 then
     while b_loc:size()>1 do b_loc:remove(0) end
     load_list()
   -- This is terrible
   else if not nofile then os.rename(listfile..".bak",listfile) end
   end
--  end
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)
     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
find_file:close()
load_list()
else updatedb(1)
end
Fl:run()

Posted by mikshaw on Jan. 17 2008,00:22
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.

Posted by mikshaw on Jan. 17 2008,19:43
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()

Posted by mikshaw on Jan. 24 2008,22:25
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()

Posted by mikshaw on Mar. 15 2008,11:46
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()

Posted by mikshaw on Mar. 15 2008,12:28
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()

Posted by mikshaw on Mar. 15 2008,14:45
More minor changes. The Tabs were replaced by Fl_Wizard. This allows the previously offscreen buttons to be used onscreen so there is only one control for switching tabs. This also gave me a way to display the last update time in the bottom right instead of printing it every time a new info is loaded.

The date check now uses murgaLua builtins instead of running ls | awk in a separate process.

Removed the 5-pixel gap between the text fields and the top/bottom widgets.

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
                  Database date check uses os.date/lfs instead of os.popen/ls/awk
                  Use wizard instead of tabs for better use of resources
      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
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
last_update:label("last update "..os.date("%d %b %Y",lfs.attributes(listfile).modification))
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=500; wh=360; bh=30; bw=ww/2-5
win=fltk:Fl_Window(ww,wh,"MyDSL Info Browser")

tabs=fltk:Fl_Wizard(0,0,ww,wh-bh-5)

tab1=fltk:Fl_Group(0,0,ww,wh-bh-5,"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)
     tab_browse:setonly()
     break
   end
 end
end
)

info_display=fltk:Fl_Text_Display(5,5+bh,ww-10,wh-(10+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-5,"search text")
search_text=fltk:Fl_Input(85,5,ww-90,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("@B49@b@uPackage Name"..spacer.."@B49@b@uLocation")
   else
     -- default screen font does not have bold or underline
     -- and no columns means only one format string is used
     results:add("@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,5+bh,ww-10,wh-(10+bh*2))
-- 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
results:textfont(fltk.FL_SCREEN) -- fixed-width makes it closer to columns
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

-- using buttons instead of tabs gives better keyboard control
tabbuttons=fltk:Fl_Pack(5,wh-bh,200,bh-5)
tabbuttons:type(fltk.FL_HORIZONTAL)
tabbuttons:spacing(5)
tab_browse=fltk:Fl_Radio_Button(0,0,100,bh-5,"&browse")
tab_browse:when(fltk.FL_WHEN_CHANGED)
tab_browse:box(fltk.FL_THIN_UP_BOX)
tab_browse:callback(function() tabs:value(tab1) end)
tab_search=fltk:Fl_Radio_Button(0,0,100,bh-5,"text &search")
tab_search:when(fltk.FL_WHEN_CHANGED)
tab_search:box(fltk.FL_THIN_UP_BOX)
tab_search:callback(
function()
 tabs:value(tab2)
 Fl:focus(search_text)
end
)
fltk:Fl_End() --end tabs buttons
last_update=fltk:Fl_Box(bw+5,wh-bh-5,bw,bh+5)

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()

Posted by mikshaw on Mar. 16 2008,02:56
I was messing around with extra colums in the results output, and when using the release date as a result, Xfree86-devel.unc is the only one that screws up. I can't figure out why this is.
Posted by roberts on Mar. 16 2008,04:08
Interestingly enough, as you may be aware,  I am working on an sql version of this.

One extension that I was using as a test case is testing/compile-3.3.5.uci.

When using your latest mydsl info parser text search, I type compile and no compile-3.3.5.uci shows up? Your code appears to be doing a full text search including title (text=s)

It is interesting the two approaches that we have taken. I showed John my sql version during Scale and it was received as "possibly too dificult to use". I prefer the fine grained searching capabilitities of sql. But I realize that many users may not be comfortable typing sql queries. So I am adding a QBE tab. Still the state of the info files are not very structured and I am not an advanced sql user. It has been a real challenge to break the info files into sql tables and fields.

Posted by mikshaw on Mar. 16 2008,04:39
Quote
I type compile and no compile-3.3.5.uci shows up
I did that exact thing a couple days ago, and thought maybe the extension disappeared. I also checked the repository and didn't find it, though.
However, I just tried "compile" again just now and compile-3.3.5.uci showed up. Odd.

I've been trying to do straight case-insensitive searches of the entire text, but I know there are still some issues to work out.

I know absolutely nothing about using sql, but I assume it's something that can be done entirely within murgaLua....at least if it's sqlite?

Posted by roberts on Mar. 16 2008,04:51
Yeah, I bunziped the mydslinfo.bz2 and did a grep compile and it was there. Strange.

I am doing  sql all in murgaLua. Still not sure if it will be a go or not.

Yours with a flat file is easier to use and offers full text search. Also since flat file is just text it can be bzipped to a smaller download than a sqlite database..

Once I get some progress on the QBE screen I will post mine and see what happens. Afterall, it is always about a sharing and leaning experience. The more I do, the more complex it is getting.

On yours, I think your collapsable browser would be nicer than those two drop downs. Just a suggestion.

Posted by mikshaw on Mar. 16 2008,13:06
Quote
I think your collapsable browser would be nicer than those two drop downs.
I thought about that yesterday morning, but at the time I didn't feel it was worth attempting until after making the updatedb function more reliable.


Quote
Xfree86-devel.unc is the only one that screws up.
Just noticed that it contains two "Current:" lines

Posted by roberts on Mar. 16 2008,18:31
Quote (mikshaw @ Mar. 16 2008,05:06)
Quote
I think your collapsable browser would be nicer than those two drop downs.
I thought about that yesterday morning, but at the time I didn't feel it was worth attempting until after making the updatedb function more reliable.


Quote
Xfree86-devel.unc is the only one that screws up.
Just noticed that it contains two "Current:" lines

Thanks for the update on XFree86-devel.unc. I have fixed it.

This is that sort of thing that I have been "cleaing up" to try to get data to be INSERTable to an SQL table. It has been more work that I originally thought.

Anyway, I have posted and updated mydslinfo.bz2

Posted by mikshaw on Mar. 16 2008,21:37
Quote
This is that sort of thing that I have been "cleaing up" to try to get data to be INSERTable to an SQL table.
I'm guessing that converting all dates to yyyy/mm/dd (if that's in your plan)  is going to be a pain.

Here's a version that uses the collapsible browser rather than two separate menus.

There are a few things to consider....

It hasn't been thoroughly tested. There are probably issues I haven't seen yet.

In order for it to work as is, it needs the database to retain two existing qualities (besides the obvious keywords Location, Title, etc)
1) A line break between every package
2) The database has all apps together, all games together, etc. It currently compares the Location string to an existing variable to decide whether or not to create a submenu. If the categories get mixed in the database file, there will be multiple copies of some submenu headers.

It still needs some tweaks of the keyboard control. For example, after installation or database update, you need to either hit Tab a few times or switch to search and back to browse in order to get keyboard focus to the file list.

I don't yet know a way to change the sort order of the list, which is a feature I've had in the back of my mind since the beginning. I'm hoping to be able to allow the user to list files in alphabetical or chronological order (at least in the search window), and maybe have user-defined columns, like license, author, and maybe description.

I was also considering having only one category expanded at any one time, but for some reason that was giving me a lot more trouble than I had expected.

EDIT: fixed the browser doubling up after a db update.
Also looking into an easier way to keep track of which info table index is required at any given time rather than having to run down through it each time.

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

    Changelog (yyyy/mm/dd)
      2008/03/16: Interface tweaks
      2008/03/15: Improved the update function
                  Database date check uses os.date/lfs instead of os.popen/ls/awk
                  Use wizard instead of tabs for better use of resources
      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"
list_sep="@b@B50@u"

function toggle(b)
local count,state,one,two=1
-- selected line and its label
local me,label=b:value(),b:text(b:value())
if string.find(label,"%+%s*") then
 state,one,two="expanded","%+  ","%-  "
else
 state,one,two="collapsed","%-  ","%+  "
end
b:text(me,string.gsub(label,one,two)) -- swap + and -
-- toggle all lines until reaching the next section or the browser end
while b:text(me+count) and not string.match(b:text(me+count),list_sep) do
 if state=="collapsed" then b:hide(me+count)
 else b:show(me+count)
 end
 count=count+1
end
end

function fill_list()
inputfile = io.popen("bunzip2 -c "..listfile)
data=inputfile:read("*a")
inputfile:close()
if not string.find(data,"%w") then -- if there's nothing readable in it
 info_display_buffer:text("Cannot read database "..listfile.."\nPlease try updating again.")
else
filelist:clear()
header={}
info={} -- table containing all package data: info[index].{location,title,date}
info_count=0 -- number of packages
local current_loc="" -- location string
local headers=0 -- nuber of locations
-- breaks up info for each package 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"),
 date=string.match(s,"Current:%s*(.-%d)%s"),
 text=s
 }
 if current_loc ~= info[info_count].location then
   -- create a header when location changes
   current_loc=info[info_count].location
   filelist:add(list_sep.."-  "..current_loc)
   headers=headers+1
   header[headers]=filelist:size()
 end
 filelist:add(info[info_count].title)
end
for i,v in ipairs(header) do filelist:value(header[i]); toggle(filelist) end
last_update:label("last update "..os.date("%d %b %Y",lfs.attributes(listfile).modification))
end
filelist:value(1)
tab_browse:setonly()
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)
   fill_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.."(.*)"..spacer)
 get_fname=string.match(results:text(results:value()),"(.-)"..spacer)
-- select the package in filelist
 local myloc=1
 while myloc<=filelist:size() do
   if not string.find(filelist:text(myloc),"  "..get_loc,1,1) then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 -- open submenu if not open
 if string.find(filelist:text(myloc),list_sep.."+  ",1,1) then toggle(filelist) end
 while myloc<=filelist:size() do
   if filelist:text(myloc) ~= get_fname then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 for i=1,info_count do
   -- make sure you get the right info AND category
   if info[i].title == get_fname and info[i].location == get_loc then
     info_display_buffer:text(info[i].text)
     tab_browse:setonly()
     myfile=info[i] -- this is the file that will be downloaded
     break
   end
 end
 tabs:value(tab1)
end
end

function list_cb()
if filelist:value()>0 then -- clicking a blank space can be fatal without this check
 if Fl:event() == fltk.FL_RELEASE or Fl:event_key()==fltk.FL_Enter then -- on click or Enter
   if string.find(filelist:text(filelist:value()),list_sep) then
     toggle(filelist) -- if it's a separator
   else
   -- find out what submenu we're under
     local myloc=filelist:value()
     while myloc > 0 do
       myloc=myloc-1
       if string.find(filelist:text(myloc),list_sep) then
         myloc=string.gsub(filelist:text(myloc),".-%s%s","")
         break
       end
     end
     for i=1,info_count do
       -- make sure you get the right info AND category
       if info[i].title == filelist:text(filelist:value()) and info[i].location == myloc then
         info_display_buffer:text(info[i].text)
         myfile=info[i]
         tab_browse:setonly()
         break
       end
     end
   end
 end
end
end

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

tabs=fltk:Fl_Wizard(0,0,ww,wh-bh-5)
tab1=fltk:Fl_Group(0,0,ww,wh-bh-5,"browse")

tiles=fltk:Fl_Tile(5,5+bh,ww-10,wh-(15+bh*2))
filelist=fltk:Fl_Hold_Browser(5,5+bh,ww/2-5,wh-(15+bh*2))
filelist:callback(list_cb)
info_display=fltk:Fl_Text_Display(ww/2,5+bh,ww/2-5,wh-(15+bh*2))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)
fltk:Fl_End() --end tiles

-- offscreen button that allows the list to respond to Enter key
enter=fltk:Fl_Return_Button(ww,wh,10,10)
enter:callback(function() filelist:do_callback() end)

install=fltk:Fl_Button(ww/2,5,bw,bh,"&Install Selected Package")
install:callback(
function()
if myfile then
 mydslload=os.execute("mydsl-load "..myfile.title.." "..myfile.location)
 if mydslload~=0 then fltk.fl_alert(myfile.title..":\nDownload or Checksum error!") end
end
end
)

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

tab2=fltk:Fl_Group(0,0,ww,wh-bh-5,"search text")
search_text=fltk:Fl_Input(85,5,ww-90,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("@B49@b@uPackage Name"..spacer.."@B49@b@uLocation"..spacer.."@B49@b@uRelease Date")
   else
     -- default screen font does not have bold or underline
     -- and no columns means only one format string is used
     results:add("@B49PACKAGE NAME"..spacer.."LOCATION"..spacer.."RELEASE DATE")
   end
   for i=1,info_count do
     if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,true) then
       if not info[i].date then info[i].date="" end
       results:add(info[i].title..spacer..info[i].location..spacer..info[i].date)
     end
   end
 end
end
)

results=fltk:Fl_Hold_Browser(5,5+bh,ww-10,wh-(15+bh*2))
-- column widths available in 0.6.4+
if Fl_Browser.column_widths then
 results:column_widths({results:w()/2,200,0})
 spacer="\t"
else spacer="\t\t" -- force some extra space for older versions
results:textfont(fltk.FL_SCREEN) -- fixed-width makes it closer to columns
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

-- using buttons instead of tabs gives better keyboard control
tabbuttons=fltk:Fl_Pack(5,wh-bh,200,bh-5)
tabbuttons:type(fltk.FL_HORIZONTAL)
tabbuttons:spacing(5)
tab_browse=fltk:Fl_Radio_Button(0,0,100,bh-5,"&browse")
tab_browse:when(fltk.FL_WHEN_CHANGED)
tab_browse:box(fltk.FL_BORDER_BOX); tab_browse:selection_color(7)
tab_browse:callback(function() tabs:value(tab1) end)
tab_search=fltk:Fl_Radio_Button(0,0,100,bh-5,"text &search")
tab_search:when(fltk.FL_WHEN_CHANGED)
tab_search:box(fltk.FL_BORDER_BOX); tab_search:selection_color(7)
tab_search:callback(
function()
 tabs:value(tab2)
 Fl:focus(search_text)
end
)
fltk:Fl_End() --end tabs buttons
last_update=fltk:Fl_Box(bw+5,wh-bh-5,bw,bh+5)


win:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tab1:resizable(tiles)
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()
 fill_list()
else updatedb()
end
Fl:run()

Posted by roberts on Mar. 16 2008,22:52
I handled all the different date formats with standard Lua captures while building and inserting to sqlite.

I think it only fair for me to show you mine since we may want to join some features especially if you are wanting to sort etc.

I started by using your GUI and stripping all flatfile code.
I added some "when" events and used "pack" on some layouts.
I stared by creating a simple database ignoring comments and other "multiline/multivalue" fields.
I then wrote standard lua and luasqlite as binding was available in murgaLua to query, display an index of 'hits' and then a view.
Later I moved the GUI around using a menu for update and quit.
Added the "comments" to database. Added QBE screen, still working on it.

This is a work in progress and I post it only as a preview. The next build of the database will break everything.

I post mainly for mikshaw but anyone can preview it. It currently only support direct sql queries. If you don't know sql don't preview it. The QBE is not implemented yet.

Of course you can download the mydsl.db open a shell start sqlite and issue sql commands too. murgaLua does not currently support readline in Linux so expect to do alot of CLI typing.

Open to suggestions on either the SQL database and the supporting GUI.

I am thinking that the multiline change-log and comments need to be separate tables.

I am not asking anyone to finish it as I am currently working on many parts. I start many sections of code before I "button" things up. That's just how I "work" er, play.

Oh, and this works with DSL 4.2.5 or DSL 3.4.11 no need to grab latest murgaLua

Code Sample


#!/bin/murgaLua
--[[
 MyDSL Extension Browser, 2008 mikshaw - SQL and other GUI mods by Robert Shingledecker
 2008/02/26: Added packed comments (probably needs sep table
 2008/02/16: Added menu for Update database and Quit.
 2008/02/14: Created QBE tab based on feedback not finished.
 2008/02/08: Basic SQL queries working - take to Scale6x to show John.
 2008/01/28: Dropped columns and switched to vertical VIEW layout.
 2008/01/24: Created sqlite database mydsl.db with only simple cols.
 2008/01/21: Gutted all flat file logic kept some GUI.
 2008/01/13: Started from mikshaw's 2008 mydsl info browser.
]]--

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

function doSQL()
if inputSQL:value() ~= "" then
  results:clear()
  selected = {}
  field_names = {}
  stmt = db:prepare(inputSQL:value())
  if stmt == nil then
     fltk.fl_alert(db:errmsg())
  else
     field_names = stmt:get_names()
     for row in stmt:rows(inputSQL:value()) do
        results:add(row[1].."\t"..row[2])
        selected[#selected+1] = row
     end
     stmt:finalize()
  end
end
end

function showSQL()
  if results:value() > 0 then
     viewResults = ""
     for i = 1, #field_names do
        if field_names[i] == "comments" then
           selected[results:value()][i] = string.gsub(selected[results:value()][i],"%^","\n")
        end
        viewResults = viewResults .. field_names[i].."\t"
        viewResults = viewResults .. selected[results:value()][i].."\n"
     end
     info_display:value(viewResults)
     tabs:value(tab1)
     tab1:hide()
     tab3:show()
  end
end


ww=420; wh=360; bh=25
w=fltk:Fl_Window(ww,wh,"MyDSL Extension Browser")
w:callback(
function(w)
 db:close()
 os.exit(0)
end)

mb = fltk:Fl_Menu_Bar(0,0,ww,bh)
mb:add("&File/&UpdateDB",0,updatedb)
mb:add("&File/E&xit",0,quit)
mb:callback(menu_items)

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

tab1=fltk:Fl_Pack(1,bh+1,ww,0,"SQL")
inputSQL=fltk:Fl_Input(0,0,ww-10,bh)
inputSQL:when(fltk.FL_WHEN_ENTER_KEY_ALWAYS)
inputSQL:callback(doSQL)

results=fltk:Fl_Select_Browser(0,0,ww-10,wh-(8+bh*3))
results:textfont(fltk.FL_SCREEN)
results:textsize(12)
results:when(fltk.FL_WHEN_CHANGED)
results:callback(showSQL)
fltk:Fl_End()

tab2=fltk:Fl_Group(0,0,ww,wh-bh,"QBE")
field_label = {"loc","title","desc","ver","author","site","policy","user","current","comments"}
field_len = {120,180,325,240,320,290,325,285,110,335}
field_in = {}
for i = 1, #field_label do
 field_in[i] = fltk:Fl_Input(75,28+(28*(i-1)),field_len[i],bh,field_label[i])
end
okBtn = fltk:Fl_Return_Button(340, 310, 70, 20, "OK");
okBtn:callback(
function(okBtn)
 for i = 1, #field_label do
   if field_in[i]:value() ~= "" then
     print(field_label[i] .." ".. field_in[i]:value())
   end
 end
end)
fltk:Fl_End()

tab3=fltk:Fl_Group(0,0,ww,wh-bh,"View")
info_display=fltk:Fl_Multiline_Output(4,bh+4,ww-10,wh-(30+bh*2))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)

install=fltk:Fl_Return_Button(340,310,70,20,"Install")
install:callback(
function()
os.execute("mydsl-load "..selected[results:value()][2].." "..selected[results:value()][1])
end
)

w:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tab1:resizable(info_display)
tab2:resizable(results)
w:show()

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

database = mydsl_dir.."/mydsl.db"
dburl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/mydsl.db"
databaseCheck = io.open(database)
if databaseCheck then
  databaseCheck:close()
else
  updatedb(1)
end

selected = {}
field_names = {}
db = sqlite3.open(database)
inputSQL:value("select * from mydsl order by loc;")

Fl:run()

Posted by mikshaw on Mar. 16 2008,22:58
Thanks for sharing. I'll try it very soon.

Quote
Oh, and this works with DSL 4.2.5 or DSL 3.4.11 no need to grab latest murgaLua
Are you implying there is trouble with my experiment beyond the lack of columns in murgaLua versions prior to 0.6.4? I tried to make sure it works in 0.5.5, but maybe I missed?

Posted by mikshaw on Mar. 16 2008,23:22
Also, sort of off-topic...

What is it that mydsl-load does initially that creates a pause before it begins to download?  When I'm not connected to the internet it takes something like 10 seconds or so before wget starts. If I'm connected it's not nearly that bad, but still takes a couple of seconds.

Posted by mikshaw on Mar. 17 2008,18:03
I tried the sql version a little, although I don't yet understand how some of the features will be used.  It was a little odd initially...I was offline and it created an empty database. After going online I had to manualy delete the database before it would download a new one.
____

Made more changes to the interface of the plain-text version. The file list can now be toggled with Alt+L. This was frustrating me for a long time because no matter what I did I couldn't get it to work if the list width was scaled smaller than its original width. I ended up creating it tiny and then resizing before showing the window. It looks like it might be a FLTK bug.

After running update or install, the filelist now gets back focus. This is to improve keyboard control.

The release date shown in the search results is formatted yyyy/mm/dd if the info originally is in mm/dd/yyyy or mm-dd-yyyy format. If the info has dd/mm/yyyy format, it will not convert properly...I don't think its possible to be 100% accurate in this case.  I hope this will not be needed at some point in the future.

The "last update" box now also displays the total number of packages in the database.

I still want to find a way to grab the browser line that is associated witha specific info table index to make the code a little more streamlined, but so far my tests have caused more problems than they solve.

EDIT: added a function to jump from section to section in the browser, [ to go backward and ] to go forward

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

    Changelog (yyyy/mm/dd)
      2008/03/17: More interface tweaks
                  Convert date format to yyyy/mm/dd (hopefully a temp fix)
      2008/03/16: Interface tweaks
      2008/03/15: Improved the update function
                  Database date check uses os.date/lfs instead of os.popen/ls/awk
                  Use wizard instead of tabs for better use of resources
      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"
list_sep="@b@B50@u"

function toggle(b)
local count,state,one,two=1
-- selected line and its label
local me,label=b:value(),b:text(b:value())
if string.find(label,"%+%s*") then
 state,one,two="expanded","%+  ","%-  "
else
 state,one,two="collapsed","%-  ","%+  "
end
b:text(me,string.gsub(label,one,two)) -- swap + and -
-- toggle all lines until reaching the next section or the browser end
while b:text(me+count) and not string.match(b:text(me+count),list_sep) do
 if state=="collapsed" then b:hide(me+count)
 else b:show(me+count)
 end
 count=count+1
end
end

function fill_list()
inputfile = io.popen("bunzip2 -c "..listfile)
data=inputfile:read("*a")
inputfile:close()
if not string.find(data,"%w") then -- if there's nothing readable in it
 info_display_buffer:text("Cannot read database "..listfile.."\nPlease try updating again.")
else
filelist:clear()
header={}
info={} -- table containing all package data: info[index].{location,title,date}
info_count=0 -- number of packages
local current_loc="" -- location string
local headers=0 -- nuber of locations
-- breaks up info for each package into a separate string
for s in string.gmatch(data,"(Location:.-)\nLocation:") do
 -- convert mm/dd/yyyy to yyyy/mm/dd
 local datestring=string.match(s,"Current:%s*(.-%d)%s")
 if string.find(datestring,"%d%d%D%d%d%D%d%d%d%d") then datestring=string.gsub(datestring,"(%d%d)%D(%d%d)%D(%d%d%d%d)","%3/%1/%2") end
 info_count=info_count+1
 info[info_count]={
 location=string.match(s,"Location:%s*(.-)%s*\n"),
 title=string.match(s,"Title:%s*(.-)%s*\n"),
 date=string.gsub(datestring,"%D","/"),
 --datestring=string.match(s,"Current:%s*(.-%d)%s"), -- plain
 text=s
 }
--  if not info[info_count].date then info[info_count].date="" end
 if current_loc ~= info[info_count].location then
   -- create a header when location string changes
   current_loc=info[info_count].location
   filelist:add(list_sep.."-  "..current_loc)
   headers=headers+1
   header[headers]=filelist:size()
 end
 filelist:add(info[info_count].title)
end
for i,v in ipairs(header) do filelist:value(header[i]); toggle(filelist) end
last_update:label("last update "..os.date("%d %b %Y",lfs.attributes(listfile).modification)..", "..info_count.." packages")
end
filelist:value(1)
tab_browse:setonly()
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)
 fill_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
filelist:take_focus() -- for better keyboard control
end

function pick_search_result()
-- show selected info file in the browse tab
if results:value() > 1 then
 get_loc=string.match(results:text(results:value()),spacer.."(.*)"..spacer)
 get_fname=string.match(results:text(results:value()),"(.-)"..spacer)
-- select the package in filelist
 local myloc=1
 while myloc<=filelist:size() do
   if not string.find(filelist:text(myloc),"  "..get_loc,1,1) then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 -- open submenu if not open
 if string.find(filelist:text(myloc),list_sep.."+  ",1,1) then toggle(filelist) end
 while myloc<=filelist:size() do
   if filelist:text(myloc) ~= get_fname then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 for i=1,info_count do
   -- make sure you get the right info AND category
   if info[i].title == get_fname and info[i].location == get_loc then
     info_display_buffer:text(info[i].text)
     tab_browse:setonly()
     myfile=info[i] -- this is the file that will be downloaded
     break
   end
 end
 tabs:value(tab1)
end
end

function list_cb()
if filelist:value()>0 then -- clicking a blank space can be fatal without this check
 if Fl:event() == fltk.FL_RELEASE or Fl:event_key()==fltk.FL_Enter then -- on click or Enter
   if string.find(filelist:text(filelist:value()),list_sep) then
     toggle(filelist) -- if it's a separator
   else
   -- find out what submenu we're under
     local myloc=filelist:value()
     while myloc > 0 do
       myloc=myloc-1
       if string.find(filelist:text(myloc),list_sep) then
         myloc=string.gsub(filelist:text(myloc),".-%s%s","")
         break
       end
     end
     for i=1,info_count do
       -- make sure you get the right info AND category
       if info[i].title == filelist:text(filelist:value()) and info[i].location == myloc then
         info_display_buffer:text(info[i].text)
         myfile=info[i]
         tab_browse:setonly()
         break
       end
     end
   end
 end
end
end

function toggle_list()
if listw > 0 then
 oldw=listw
 tiles:position(listw+5,nil,6,nil)
 listw=0
 info_display:take_focus()
else
 tiles:position(6,nil,oldw+5,nil)
 listw=oldw
 filelist:take_focus()
end
end

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

tabs=fltk:Fl_Wizard(0,0,ww,wh-bh-5)
tab1=fltk:Fl_Group(0,0,ww,wh-bh-5,"browse")

-- the tiles are built weird initially (filelist is only 1px wide)
-- this is a kluge; toggle_list doesn't seem to work if filelist
-- is shrunk less than it's initial width
tiles=fltk:Fl_Tile(5,5+bh,ww-10,wh-(15+bh*2))
--filelist=fltk:Fl_Hold_Browser(5,5+bh,ww/2-5,wh-(15+bh*2))
filelist=fltk:Fl_Hold_Browser(5,5+bh,1,wh-(15+bh*2))
filelist:callback(list_cb)
--info_display=fltk:Fl_Text_Display(ww/2,5+bh,ww/2-5,wh-(15+bh*2))
info_display=fltk:Fl_Text_Display(6,5+bh,ww-11,wh-(15+bh*2))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)
fltk:Fl_End() --end tiles
tiles:position(6,nil,ww/3,nil) -- resize the filelist
tiles:callback(function() listw=filelist:w() end)
listw=filelist:w()
prevheader=fltk:Fl_Button(ww,wh,bh,bh,"&[")
prevheader:callback(
function()
for i=table.getn(header),1,-1 do
 if header[i] < filelist:value() then
   filelist:value(header[i])
   break
 end
end
filelist:take_focus()
end
)
nextheader=fltk:Fl_Button(ww,wh,bh,bh,"&]")
nextheader:callback(
function()
for i=1,table.getn(header) do
 if header[i] > filelist:value() then
   filelist:value(header[i])
   break
 end
end
filelist:take_focus()
end
)
-- offscreen button that allows the list to respond to Enter key
enter=fltk:Fl_Return_Button(ww,wh,10,10)
enter:callback(function() filelist:do_callback() end)
togglist=fltk:Fl_Button(ww,wh,10,10,"&l")
togglist:callback(toggle_list)

install=fltk:Fl_Button(ww/2,5,bw,bh,"&Install Selected Package")
install:callback(
function()
if myfile then
 mydslload=os.execute("mydsl-load "..myfile.title.." "..myfile.location)
 if mydslload~=0 then fltk.fl_alert(myfile.title..":\nDownload or Checksum error!") end
end
filelist:take_focus()
end
)

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

tab2=fltk:Fl_Group(0,0,ww,wh-bh-5,"search text")
search_text=fltk:Fl_Input(85,5,ww-90,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("@B49@b@uPackage Name"..spacer.."@B49@b@uLocation"..spacer.."@B49@b@uRelease Date")
   else
     -- default screen font does not have bold or underline
     -- and no columns means only one format string is used
     results:add("@B49PACKAGE NAME"..spacer.."LOCATION"..spacer.."RELEASE DATE")
   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..spacer..info[i].date)
     end
   end
 end
end
)

results=fltk:Fl_Hold_Browser(5,5+bh,ww-10,wh-(15+bh*2))
-- column widths available in 0.6.4+
if Fl_Browser.column_widths then
 results:column_widths({results:w()/2,200,0})
 spacer="\t"
else spacer="\t\t" -- force some extra space for older versions
results:textfont(fltk.FL_SCREEN) -- fixed-width makes it closer to columns
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

-- using buttons instead of tabs gives better keyboard control
tabbuttons=fltk:Fl_Pack(5,wh-bh,200,bh-5)
tabbuttons:type(fltk.FL_HORIZONTAL)
tabbuttons:spacing(5)
tab_browse=fltk:Fl_Radio_Button(0,0,100,bh-5,"&browse")
tab_browse:when(fltk.FL_WHEN_CHANGED)
tab_browse:box(fltk.FL_BORDER_BOX); tab_browse:selection_color(7)
tab_browse:callback(function() tabs:value(tab1) end)
tab_search=fltk:Fl_Radio_Button(0,0,100,bh-5,"text &search")
tab_search:when(fltk.FL_WHEN_CHANGED)
tab_search:box(fltk.FL_BORDER_BOX); tab_search:selection_color(7)
tab_search:callback(
function()
 tabs:value(tab2)
 Fl:focus(search_text)
end
)
fltk:Fl_End() --end tabs buttons
--last_update=fltk:Fl_Box(bw+5,wh-bh-5,bw,bh+5)
last_update=fltk:Fl_Box(ww-5,wh-bh-5,5,bh+5)
last_update:align(fltk.FL_ALIGN_LEFT)


win:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tab1:resizable(tiles)
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()
 fill_list()
else updatedb()
end
Fl:run()

Posted by roberts on Mar. 18 2008,02:34
Quote
I tried the sql version a little, although I don't yet understand how some of the features will be used.  It was a little odd initially...I was offline and it created an empty database. After going online I had to manualy delete the database before it would download a new one.


Interesting that you would think an initial offline status would work.
Not that I needed debugging there. Beside use of the File menu ->UpdateDB would fetch it when you are online. No need to manually delete it.

The flow of the GUI is to type an valid SQL statement and upon presssing Enter, a hit list is displayed. Selecting one from such list switches to the View tab displaying the fields select in the SQL statement. Whereupon pressing enter will install the extension or select SQL one can make another choice from the displayed hit list or even decide to further expand or refine the SQL statement.

The QBE will be used to automatcially build the SQL statement via entering the data via corresponding fields. Kinda like SQL "training wheels". Once again the hit list and the generated SQL will be shown and selecting one from the hit will display the view of all fields selected just as before.

Posted by roberts on Mar. 18 2008,02:39
Quote (mikshaw @ Mar. 16 2008,14:58)
Thanks for sharing. I'll try it very soon.

Quote
Oh, and this works with DSL 4.2.5 or DSL 3.4.11 no need to grab latest murgaLua
Are you implying there is trouble with my experiment beyond the lack of columns in murgaLua versions prior to 0.6.4? I tried to make sure it works in 0.5.5, but maybe I missed?

All of your scripts specify
Quote
#!/home/dsl/bin/murgaLua-0.6.4

Which would naturally lead one such assumption as it is not needed in DSL unless features that are only available in 0.6.4.were being attempted.

Posted by roberts on Mar. 18 2008,02:42
Quote (mikshaw @ Mar. 16 2008,15:22)
Also, sort of off-topic...

What is it that mydsl-load does initially that creates a pause before it begins to download?  When I'm not connected to the internet it takes something like 10 seconds or so before wget starts. If I'm connected it's not nearly that bad, but still takes a couple of seconds.

Many factors here including but not limited to:
Multiple entry points to the mydsl loading system to support CLI as well as GUI. A mix of many shells and lua scripts being called. And the internet host name lookup based on configuration file. All of this before the actual download begins.

Posted by mikshaw on Mar. 19 2008,12:07
Quote (roberts @ Mar. 17 2008,21:39)
All of your scripts specify
Quote
#!/home/dsl/bin/murgaLua-0.6.4

Which would naturally lead one such assumption as it is not needed in DSL unless features that are only available in 0.6.4.were being attempted.

I see.

I use the newer interpreter when testing newer features. It is not a requirement, though. Anything I write for use in DSL should be useable with murgaLua 0.5.5. The features of newer versions are used only if they are available....
Code Sample
if Fl_Browser.column_widths then
 results:column_widths({results:w()/2,150,0})
 spacer="\t"
else spacer="\t\t" -- force some extra space for older versions


Sometimes I remove the interpreter line when uploading, but usually I don't think about it.

Posted by roberts on Mar. 30 2008,03:52
Might be an issue with os.rename and the new mydslBrowser.lua formerly known as mydsl info parser.

It becomes apparent with the standard grub pendive installation, where the first partition is fat32 and the second is ext2.

Upon first invocation of the script, you can see the download of the mydslinfo.bz2 occur via wget but the result is:

"Cannot read database /mnt/sda1/mydsl/mydslinfo.bz2"
"Please try updating again.

Upon each attempt at such results in the same message, each with another copy of mydslinfo.bz2 named as a lua tempfile. in the /tmp directory  An endless loop.

Appears that line:

os.rename(tempfile,listfile)

quietly fails.

This can duplicated by:
Code Sample

#!/bin/lua
listfile = "/mnt/sda1/mydsl/mydslinfo.bz2"
tempfile = "/tmp/lua_xCjkEa"
os.rename(tempfile,listfile)


Where /mnt/sda1 is fat32 and /tmp is ext2.

This issue involves 4.3RC1.

This was not an issue in early versions of the script.

Posted by mikshaw on Mar. 30 2008,10:34
I assume "early versions" would be prior to March 15, when the messy version (i.e. overwriting with an empty file) of the updatedb function was still being used.

Considering the apparent simplicity of the function, I can't see how the script itself is faulty (not saying I'm genius of course).
Perhaps there is an issue with fat32 + os.rename. The error you mentioned occurs if the database file is empty or non-existent, so I'm guessing it's not being moved. I can't think of any reason for this other than a problem with rename.

I assume the temp file download completes, since you didn't get the error "Database check failed".

What happens if you replace os.rename(tempfile,listfile) with os.execute("sudo mv -f "..tempfile.." "..listfile)?

Posted by roberts on Mar. 30 2008,13:41
I think that what I said with the first six words that I posted as well as the code posted. os.rename fails. I was just pointing that fact out.

Naturally the native mv works across different filesystems or we would all in trouble

sudo is not needed with the os.execute of the mv command as the target mydsl/ is setup with proper permissions.

I will make that adjustment to your script.

Posted by mikshaw on Mar. 30 2008,13:54
Quote
sudo is not needed with the os.execute of the mv command as the target mydsl/ is setup with proper permissions.
I just thought there might be a chance of problems with moving (as opposed to copying) from fat32. I have no experience with it, and know you're smart enough to notice that sudo is there =o)

Thanks. I'll make the change on my end too.

Posted by roberts on Mar. 30 2008,15:02
At the bottom it displays 450 packages.
Yet upon bunzip mydslinfo.bz2 and performing

grep "^Title:" mydslinfo | wc -l

I get far more than 450. There are more than 450 packages in the repository.

[EDIT] After checking my local copy vs remote (ibiblio) vs grep vs find -name "*.info" the correct total "info" files as of this posting is 893



Posted by mikshaw on Mar. 31 2008,01:04
I had a feeling it wasn't working as I'd hoped.
I got 901 lines using  bunzip2 -c mydslinfo.bz2 |grep "^Title:" | wc -l

Not sure how to approach the problem at this time, but I'll look into it soon.

Posted by roberts on Mar. 31 2008,01:20
Yeah it was 901. I had a mismatch from the recently deleted extensions. If you update database you should get 893.
Both local and remote directories are in complete sync.

Posted by mikshaw on Mar. 31 2008,10:50
893 now, yes.
446 listed in the gui...definitely something weird, must be either still cramming infos together or just dropping some of it. I haven't found anything close to an answer yet. I think I'm going to do a test with parsing the file line by line instead of reading the whole thing in. It might actually be faster, and probably easier to debug.

The "load local" mentioned in the RC thread might be a *little* less than easy, considering the application automatically tries to connect when it starts. If local was just plugged in, a user with a system not connected would have to wait several seconds every time the application is started, or manually close the terminal window every time.

I was thinking of a few possibilities:

1) Start the interface with a fl_choice -- local or web -- and load the main window only if you choose the internet.
2) Commandline/env option to prevent the initial update from running
3) Update automatically, but ask before doing it. This would be the easiest to implement of these 3 options.

Posted by mikshaw on Mar. 31 2008,11:59
446 is half of 893-1 (-1 being the last search for Location:)
I know where the problem is, but I'm not sure how to fix it at the moment...it will require a different search string for gmatch.

Code Sample
for s in string.gmatch(data,"\n(Location:.-)\nLocation:") do
I thought the capture would be the only part of the actual match, but I think I was wrong. It seems that the gmatch function iterates over the input looking for "\n(Location:.-)\nLocation:" and just returns the capture, but that whole string is used as the match. So what it's doing is capturing every other info.

So far I haven't come up with a reliable search string.

Posted by mikshaw on Mar. 31 2008,13:47
Code Sample
for s in string.gmatch(data,"(Location:.-\n)\n") do

This returns 982 packages.

Code Sample
bunzip2 -c mydslinfo.bz2|grep -B1 "^Location:"|grep  "^ .*$"

This shows the xorg72.uci and xpdf-3.02.uci are not separated by a blank line as all the others are.
The Xorg info swallows Xpdf

I'll work on the load-local this afternoon or evening. Please let me know how you'd prefer it to be included.

Posted by roberts on Mar. 31 2008,14:25
Glad to hear.  I will look at those extensions info files and try to correct on the mydslinfo side.

On the Load Local, what I had in mind was a third button at the top of the display.

Then upon invocation if no database has been downloaded, show all buttons top and bottom ghosted out except "Update Database" and "Load Local" and perhaps a text message of "No database currently loaded" where you were showing the database date and count.

That way no delay and user controls when to try to "fetch" the database.



Posted by roberts on Mar. 31 2008,15:21
xorg72.uci.info has no eol. Which explains why xorg72.uci,info swallowd xpdf-3.0.2. I corrected xorg72.uci.info and uploaded a new mydslinfo.bz2
Posted by Juanito on Mar. 31 2008,15:57
Quote
xorg72.uci.info has no eol

- I guess eol = end of line, but for future reference and since I am the guilty party, could you explain the error in a little more detail?

Posted by roberts on Mar. 31 2008,16:39
Usually caused by the editor that was used in the creation of the info file. Not something that you particually caused. When I opened it in vi I could see the noeol indicator. I just re-filed it.

If you can recall, what editor did you use?

Posted by mikshaw on Mar. 31 2008,16:49
Quote
On the Load Local, what I had in mind was a third button at the top of the display.
That's in already =o)

Quote
Then upon invocation if no database has been downloaded, show all buttons top and bottom ghosted out except "Update Database" and "Load Local" and perhaps a text message of "No database currently loaded" where you were showing the database date and count.
Ok, disabling the buttons should be a snap, though i hadn't thought of that so it's not currently done.

Here's what I did before reading your post...
If there is no database fl_choice asks if you want to download it.
Cancelling the popup just displays the interface as usual, with no data displayed. The buttons that will be disabled are currently programed to do nothing if there is no applicable data to use, so at least it shouldn't break if the user tries them.

So this is what I've got so far.
Code Sample
#!/bin/murgaLua
--[[
    MyDSL browser, 2008 mikshaw

    Changelog (yyyy/mm/dd)
      2008/03/31: Replaced os.rename() with os.execute("mv") (thanks roberts)
                  All infos are displayed (fixed bad gmatch string)
                  Added "load local" button
      2008/03/18: Added help screen
      2008/03/17: More interface tweaks
                  Convert date format to yyyy/mm/dd (hopefully a temp fix)
      2008/03/16: Interface tweaks
      2008/03/15: Improved the update function
                  Database date check uses os.date/lfs instead of os.popen/ls/awk
                  Use wizard instead of tabs for better use of resources
      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"
list_sep="@b@B49@u"

help_text=[[

@sMyDSL Browser, Version 1?
@-
@bAbout

This program is used to view details of available myDSL extensions,
and to install a selected package. An internet connection is required.

The first time the program runs it will attempt to download a database
from a myDSL server, which will be saved to the directory listed in
/opt/.mydsl_dir

Package installation is done using the existing myDSL system, which
connects to the server listed in /opt/.dslrc
@-
@bBasic Usage

Choose a package to view either by browsing the file list or by
entering a text string to search, and download and install that
package if you discover that you must have it.

Text search of the entire database is available by clicking on the "search"
button (Alt+S).  The search is case-insensitive, but is otherwise literal,
meaning that no translation is done for special characters like "*" or "?".
Clicking on a search result will display the info for that package.

The database can be retrieved at any time by clicking "Update" (Alt+U).
If the download fails the program continues to use the current database.

The "Install" button (Alt+I) attempts to download the package displayed
in the info window, not necessarily the one highlighted in the file list.

The "Install Local" button lets you browse for packages saved on a disk.
@-
@bKeyboard

In addition to the key controls marked on the various buttons, there are
some keys available to make keyboard control easier. In most cases the
use of "Alt" is not needed in this application, and pressing just the
appropriate key should work. When the search text input field has focus,
however, it is necessary to Use "Alt" in addition to the key.

Enter : Activate the selected item in the file list or search
T     : Toggle the visibility of the file list
]     : Jump to next section in the file list.
[     : Jump to previous section in the file list.

These keys are not global. The widget that will receive the desired
action must be onscreen at the time. For example, the database will
not be updated if you press U when the main tab (browse) is not visible.
@-
@sCopyright (c) 2008 mikshaw
@sThis script is free software, released without warranty and with no guarantee that it will do anything useful.

]]

function toggle(b)
local count,state,one,two=1
-- selected line and its label
local me,label=b:value(),b:text(b:value())
if string.find(label,"%+%s*") then
 state,one,two="expanded","%+  ","%-  "
else
 state,one,two="collapsed","%-  ","%+  "
end
b:text(me,string.gsub(label,one,two)) -- swap + and -
-- toggle all lines until reaching the next section or the browser end
while b:text(me+count) and not string.match(b:text(me+count),list_sep) do
 if state=="collapsed" then b:hide(me+count)
 else b:show(me+count)
 end
 count=count+1
end
end

function fill_list()
inputfile = io.popen("bunzip2 -c "..listfile)
data=inputfile:read("*a")
inputfile:close()
if not string.find(data,"%w") then -- if there's nothing readable in it
 info_display_buffer:text("Cannot read database "..listfile.."\nPlease try updating again.")
else
filelist:clear()
header={}
info={} -- table containing all package data: info[index].{location,title,date}
info_count=0 -- number of packages
local current_loc="" -- location string
local headers=0 -- number of locations
-- breaks up info for each package into a separate string
locount=0
for s in string.gmatch(data,"(Location:.-\n)\n") do
 -- convert mm/dd/yyyy to yyyy/mm/dd
 local datestring=string.match(s,"Current:%s*(.-%d)%s")
 if string.find(datestring,"%d%d%D%d%d%D%d%d%d%d") then datestring=string.gsub(datestring,"(%d%d)%D(%d%d)%D(%d%d%d%d)","%3/%1/%2") end
 info_count=info_count+1
 info[info_count]={
 location=string.match(s,"Location:%s*(.-)%s*\n"),
 title=string.match(s,"Title:%s*(.-)%s*\n"),
 date=string.gsub(datestring,"%D","/"),
 --datestring=string.match(s,"Current:%s*(.-%d)%s"), -- plain
 text=s
 }
 if current_loc ~= info[info_count].location then
   -- create a header when location string changes
   current_loc=info[info_count].location
   filelist:add(list_sep.."-  "..current_loc)
   headers=headers+1
   header[headers]=filelist:size()
 end
 filelist:add(info[info_count].title)
end
for i,v in ipairs(header) do filelist:value(header[i]); toggle(filelist) end
last_update:label("Database updated "..os.date("%d %b %Y",lfs.attributes(listfile).modification)..", "..info_count.." packages")
end
filelist:value(1)
tab_browse:setonly()
end

function updatedb()
-- download to temp file, check file integrity, and only then replace the old file
tempfile=os.tmpname()
os.execute(terminal.." -title \"Database Update\" -e wget "..listurl.." -O "..tempfile)
if os.execute("bzip2 -t "..tempfile) == 0 then
 os.execute("mv "..tempfile.." "..listfile)
 fill_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
filelist:take_focus() -- for better keyboard control
end

function do_search()
 if search_text:value() ~= "" and info_count and info_count>0 then
   results:clear()
   if Fl_Browser.column_widths then
     results:add("@B49@b@uPackage Name"..spacer.."@B49@b@uLocation"..spacer.."@B49@b@uRelease Date")
   else
     results:add("@B49PACKAGE NAME"..spacer.."LOCATION"..spacer.."RELEASE DATE")
   end
   for i=1,info_count do
     if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,1) then
       results:add(info[i].title..spacer..info[i].location..spacer..info[i].date)
     end
   end
   results:insert(1,"@B49@."..tostring(results:size()-1).." search results")
 end
end

function pick_search_result()
-- show selected info file in the browse tab
if results:value() > 2 then
 get_loc=string.match(results:text(results:value()),spacer.."(.*)"..spacer)
 get_fname=string.match(results:text(results:value()),"(.-)"..spacer)
-- select the package in filelist
 local myloc=1
 while myloc<=filelist:size() do
   if not string.find(filelist:text(myloc),"  "..get_loc,1,1) then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 -- open submenu if not open
 if string.find(filelist:text(myloc),list_sep.."+  ",1,1) then toggle(filelist) end
 while myloc<=filelist:size() do
   if filelist:text(myloc) ~= get_fname then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 for i=1,info_count do
   -- make sure you get the right info AND category
   if info[i].title == get_fname and info[i].location == get_loc then
     info_display_buffer:text(info[i].text)
     tab_browse:setonly()
     myfile=info[i] -- this is the file that will be downloaded
     break
   end
 end
 tabs:value(tab1)
end
end

function list_cb()
if filelist:value()>0 then -- clicking a blank space can be fatal without this check
 if Fl:event() == fltk.FL_RELEASE or Fl:event_key()==fltk.FL_Enter then -- on click or Enter
   if string.find(filelist:text(filelist:value()),list_sep) then
     toggle(filelist) -- if it's a separator
   else
   -- find out what submenu we're under
     local myloc=filelist:value()
     while myloc > 0 do
       myloc=myloc-1
       if string.find(filelist:text(myloc),list_sep) then
         myloc=string.gsub(filelist:text(myloc),".-%s%s","")
         break
       end
     end
     for i=1,info_count do
       -- make sure you get the right info AND category
       if info[i].title == filelist:text(filelist:value()) and info[i].location == myloc then
         info_display_buffer:text(info[i].text)
         myfile=info[i]
         tab_browse:setonly()
         break
       end
     end
   end
 end
end
end

function toggle_list()
if listw > 0 then
 oldw=listw
 tiles:position(listw+5,nil,6,nil)
 listw=0
 info_display:take_focus()
else
 tiles:position(6,nil,oldw+5,nil)
 listw=oldw
 filelist:take_focus()
end
end

terminal="aterm +tr -bg white -fg black -geometry 80x4"
ww=560; wh=380; bh=30; bw=(ww-10)/3
win=fltk:Fl_Window(ww,wh,"MyDSL Browser")

tabs=fltk:Fl_Wizard(0,0,ww,wh-bh-5)
tab1=fltk:Fl_Group(0,0,ww,wh-bh-5,"browse")

-- the tiles are built weird initially (filelist is only 1px wide)
-- this is a kluge; toggle_list doesn't seem to work if filelist
-- is shrunk less than it's initial width
tiles=fltk:Fl_Tile(5,5+bh,ww-10,wh-(15+bh*2))
filelist=fltk:Fl_Hold_Browser(5,5+bh,1,wh-(15+bh*2))
filelist:callback(list_cb)

info_display=fltk:Fl_Text_Display(6,5+bh,ww-11,wh-(15+bh*2))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)
fltk:Fl_End() --end tiles

tiles:position(6,nil,ww/3,nil) -- resize the filelist
tiles:callback(function() listw=filelist:w() end)
listw=filelist:w() -- for toggle_list()

-- offscreen buttons for keyboard control
prevheader=fltk:Fl_Button(ww,wh,bh,bh,"&[")
prevheader:callback(
function()
for i=table.getn(header),1,-1 do
 if header[i] < filelist:value() then filelist:value(header[i]); break end
end
filelist:take_focus()
end)
nextheader=fltk:Fl_Button(ww,wh,bh,bh,"&]")
nextheader:callback(
function()
for i=1,table.getn(header) do
 if header[i] > filelist:value() then filelist:value(header[i]); break end
end
filelist:take_focus()
end)
enter=fltk:Fl_Return_Button(ww,wh,10,10)
enter:callback(function() filelist:do_callback() end)
togglist=fltk:Fl_Button(ww,wh,10,10,"&t")
togglist:callback(toggle_list)

-- top buttons
b_updatedb=fltk:Fl_Button(5,5,bw,bh,"&Update Database")
b_updatedb:callback(updatedb)
install=fltk:Fl_Button(bw+5,5,bw,bh,"&Install Selected Package")
install:callback(
function()
if myfile then
 mydslload=os.execute("mydsl-load "..myfile.title.." "..myfile.location)
 if mydslload~=0 then fltk.fl_alert(myfile.title..":\nDownload or Checksum error!") end
end
filelist:take_focus()
end)
install_local=fltk:Fl_Button(bw*2+5,5,bw,bh,"Install &Local Package  @DnArrow")
install_local:callback(
function()
local filename=fltk.fl_file_chooser("Browse for a MyDSL file","Mydsl files (*.{dsl,tar.gz,uci,unc})",mydsl_dir,nil)
if filename then os.execute("mydsl-load "..filename) end
end)
fltk:Fl_End() --end tab1

tab2=fltk:Fl_Group(0,0,ww,wh-bh-5,"search text")
search_text=fltk:Fl_Input(bh*3+5,5,ww-10-bh*3,bh,"search text ")
search_text:when(fltk.FL_WHEN_ENTER_KEY)
search_text:callback(do_search)

results=fltk:Fl_Hold_Browser(5,5+bh,ww-10,wh-(15+bh*2))
-- column widths available in 0.6.4+
if Fl_Browser.column_widths then
 results:column_widths({results:w()/2,150,0})
 spacer="\t"
else spacer="\t\t" -- force some extra space for older versions
results:textfont(fltk.FL_SCREEN) -- fixed-width makes it closer to columns
end
-- react to click only
results:callback( 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
tab3=fltk:Fl_Group(0,0,ww,wh-bh-5,"help")
help=fltk:Fl_Browser(5,5,ww-10,wh-(15+bh))
fltk:Fl_End() --end tab3
fltk:Fl_End() --end tabs group
for line in string.gmatch(help_text,".-\n") do help:add(line) end

-- using buttons instead of tabs gives better keyboard control
tabbuttons=fltk:Fl_Pack(5,wh-bh,200,bh-5)
tabbuttons:type(fltk.FL_HORIZONTAL)
tabbuttons:spacing(5)
tab_browse=fltk:Fl_Radio_Button(0,0,100,bh-5,"&browse")
tab_browse:when(fltk.FL_WHEN_CHANGED)
tab_browse:box(fltk.FL_BORDER_BOX); tab_browse:selection_color(7)
tab_browse:callback(function() tabs:value(tab1) end)
tab_search=fltk:Fl_Radio_Button(0,0,100,bh-5,"text &search")
tab_search:when(fltk.FL_WHEN_CHANGED)
tab_search:box(fltk.FL_BORDER_BOX); tab_search:selection_color(7)
tab_search:callback(function() tabs:value(tab2); Fl:focus(search_text) end)
tab_help=fltk:Fl_Radio_Button(0,0,50,bh-5,"&help")
tab_help:when(fltk.FL_WHEN_CHANGED)
tab_help:box(fltk.FL_BORDER_BOX); tab_help:selection_color(7)
tab_help:callback(function() tabs:value(tab3) end)
fltk:Fl_End() --end tabs buttons
last_update=fltk:Fl_Box(ww-5,wh-bh-5,5,bh+5)
last_update:align(fltk.FL_ALIGN_LEFT)
last_update:labelsize(12)

win:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tabs:resizable(tab3)
tab1:resizable(tiles)
tab2:resizable(results)
tab3:resizable(help)
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(); fill_list()
else
 ask4db=fltk.fl_choice("You have no database for online MyDSL apps.\nDo you want to download it now?","No","Yes",NULL)
 if ask4db==1 then Fl:check(); updatedb() end
end
Fl:run()

Posted by mikshaw on April 01 2008,15:52
I cut the size down drastically by removing some unnecessary stuff...

The help tab really didn't provide enough useful information to be worth the weight, so that's gone. It also allows everything but "update" and "install local" to be deactivated when no database is available.

The comments and changelog were removed. I'm keeping a copy with that info in case I need to refer to it in the future.

Also removed the code that checks for the existence of column_widths, which was there to support murgaLua older than 0.6.4

The script went from 15015 to 8800 bytes.

EDIT: I had tried to use io.popen to run the mydsl-load command, in order to grab the command output, and it was working well sometimes. Unfortunately mydsl-load failure (such as the "please mount" error we occasionally see) will bring down the whole gui.  Does anyone know if FLTK has a means of handling errors more gracefully than just shutting down?

Code Sample
#!/bin/murgaLua
-- MyDSL Browser, 2008 mikshaw

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"
list_sep="@b@B49@u"

function toggle(b)
local count,state,one,two=1
local me,label=b:value(),b:text(b:value())
if string.find(label,"%+%s*") then
 state,one,two="expanded","%+  ","%-  "
else
 state,one,two="collapsed","%-  ","%+  "
end
b:text(me,string.gsub(label,one,two))
while b:text(me+count) and not string.match(b:text(me+count),list_sep) do
 if state=="collapsed" then b:hide(me+count)
 else b:show(me+count)
 end
 count=count+1
end
end

function fill_list()
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
filelist:clear()
header={}
info={}
info_count=0
local current_loc=""
local headers=0
locount=0
for s in string.gmatch(data,"(Location:.-\n)\n") do
 local datestring=string.match(s,"Current:%s*(.-%d)%s")
 if string.find(datestring,"%d%d%D%d%d%D%d%d%d%d") then datestring=string.gsub(datestring,"(%d%d)%D(%d%d)%D(%d%d%d%d)","%3/%1/%2") end
 info_count=info_count+1
 info[info_count]={
 location=string.match(s,"Location:%s*(.-)%s*\n"),
 title=string.match(s,"Title:%s*(.-)%s*\n"),
 date=string.gsub(datestring,"%D","/"),
 text=s
 }
 if current_loc ~= info[info_count].location then
   current_loc=info[info_count].location
   filelist:add(list_sep.."-  "..current_loc)
   headers=headers+1
   header[headers]=filelist:size()
 end
 filelist:add(info[info_count].title)
end
for i,v in ipairs(header) do filelist:value(header[i]); toggle(filelist) end
last_update:label("Database updated "..os.date("%d %b %Y",lfs.attributes(listfile).modification)..", "..info_count.." packages")
end
filelist:value(1)
tab_browse:setonly()
end

function updatedb()
tempfile=os.tmpname()
os.execute(terminal.." -title \"Database Update\" -e wget "..listurl.." -O "..tempfile)
if os.execute("bzip2 -t "..tempfile) == 0 then
 os.execute("mv "..tempfile.." "..listfile)
 install:activate()
 tab_search:activate()
 tab_browse:activate()
 info_display_buffer:text("\nDatabase updated successfully.")
 fill_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
filelist:take_focus()
end

function do_search()
 if search_text:value() ~= "" and info_count and info_count>0 then
   results:clear()
   results:add("@B49@b@uPackage Name\t@B49@b@uLocation\t@B49@b@uRelease Date")
   for i=1,info_count do
     if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,1) then
       results:add(info[i].title.."\t"..info[i].location.."\t"..info[i].date)
     end
   end
   results:insert(1,"@B49@."..tostring(results:size()-1).." search results")
 end
end

function pick_search_result()
if results:value() > 2 then
 get_loc=string.match(results:text(results:value()),"\t(.*)\t")
 get_fname=string.match(results:text(results:value()),"(.-)\t")
 local myloc=1
 while myloc<=filelist:size() do
   if not string.find(filelist:text(myloc),"  "..get_loc,1,1) then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 if string.find(filelist:text(myloc),list_sep.."+  ",1,1) then toggle(filelist) end
 while myloc<=filelist:size() do
   if filelist:text(myloc) ~= get_fname then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 for i=1,info_count do
   if info[i].title == get_fname and info[i].location == get_loc then
     info_display_buffer:text(info[i].text)
     tab_browse:setonly()
     myfile=info[i]
     break
   end
 end
 tabs:value(tab1)
end
end

function list_cb()
if filelist:value()>0 then
 if Fl:event() == fltk.FL_RELEASE or Fl:event_key()==fltk.FL_Enter then
   if string.find(filelist:text(filelist:value()),list_sep) then
     toggle(filelist)
   else
     local myloc=filelist:value()
     while myloc > 0 do
       myloc=myloc-1
       if string.find(filelist:text(myloc),list_sep) then
         myloc=string.gsub(filelist:text(myloc),".-%s%s","")
         break
       end
     end
     for i=1,info_count do
       if info[i].title == filelist:text(filelist:value()) and info[i].location == myloc then
         info_display_buffer:text(info[i].text)
         myfile=info[i]
         tab_browse:setonly()
         break
       end
     end
   end
 end
end
end

function toggle_list()
if listw > 0 then
 oldw=listw
 tiles:position(listw+5,nil,6,nil)
 listw=0
 info_display:take_focus()
else
 tiles:position(6,nil,oldw+5,nil)
 listw=oldw
 filelist:take_focus()
end
end

terminal="aterm +tr -bg white -fg black -geometry 80x4"
ww=560; wh=380; bh=30; bw=(ww-10)/3
win=fltk:Fl_Window(ww,wh,"MyDSL Browser")

tabs=fltk:Fl_Wizard(0,0,ww,wh-bh-5)
tab1=fltk:Fl_Group(0,0,ww,wh-bh-5,"browse")

tiles=fltk:Fl_Tile(5,5+bh,ww-10,wh-(15+bh*2))
filelist=fltk:Fl_Hold_Browser(5,5+bh,1,wh-(15+bh*2))
filelist:callback(list_cb)

info_display=fltk:Fl_Text_Display(6,5+bh,ww-11,wh-(15+bh*2))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)
fltk:Fl_End()

tiles:position(6,nil,ww/3,nil)
tiles:callback(function() listw=filelist:w() end)
listw=filelist:w()

prevheader=fltk:Fl_Button(ww,wh,bh,bh,"&[")
prevheader:callback(
function()
for i=table.getn(header),1,-1 do
 if header[i] < filelist:value() then filelist:value(header[i]); break end
end
filelist:take_focus()
end)
nextheader=fltk:Fl_Button(ww,wh,bh,bh,"&]")
nextheader:callback(
function()
for i=1,table.getn(header) do
 if header[i] > filelist:value() then filelist:value(header[i]); break end
end
filelist:take_focus()
end)
enter=fltk:Fl_Return_Button(ww,wh,10,10)
enter:callback(function() filelist:do_callback() end)
togglist=fltk:Fl_Button(ww,wh,10,10,"&t")
togglist:callback(toggle_list)

b_updatedb=fltk:Fl_Button(5,5,bw,bh,"&Update Database")
b_updatedb:callback(updatedb)
install=fltk:Fl_Button(bw+5,5,bw,bh,"&Install Selected Package")
install:callback(
function()
if myfile then
 mydslload=os.execute("mydsl-load "..myfile.title.." "..myfile.location)
 if mydslload~=0 then fltk.fl_alert(myfile.title..":\nDownload or Checksum error!") end
end
filelist:take_focus()
end)
install_local=fltk:Fl_Button(bw*2+5,5,bw,bh,"Install &Local Package  @DnArrow")
install_local:callback(
function()
local filename=fltk.fl_file_chooser("Browse for a MyDSL file","Mydsl files (*.{dsl,tar.gz,uci,unc})",mydsl_dir,nil)
if filename then os.execute("mydsl-load "..filename) end
end)
fltk:Fl_End()

tab2=fltk:Fl_Group(0,0,ww,wh-bh-5,"search text")
search_text=fltk:Fl_Input(bh*3+5,5,ww-10-bh*3,bh,"search text ")
search_text:when(fltk.FL_WHEN_ENTER_KEY)
search_text:callback(do_search)

results=fltk:Fl_Hold_Browser(5,5+bh,ww-10,wh-(15+bh*2))
results:column_widths({results:w()/2,150,0})
results:callback( function() if results:value() > 1 and Fl:event()==fltk.FL_RELEASE then pick_search_result() end end)
results_cb=fltk:Fl_Return_Button(ww,wh,bh,bh)
results_cb:callback(pick_search_result)
fltk:Fl_End()
fltk:Fl_End()

tabbuttons=fltk:Fl_Pack(5,wh-bh,200,bh-5)
tabbuttons:type(fltk.FL_HORIZONTAL)
tabbuttons:spacing(5)
tab_browse=fltk:Fl_Radio_Button(0,0,100,bh-5,"&browse")
tab_browse:when(fltk.FL_WHEN_CHANGED)
tab_browse:box(fltk.FL_BORDER_BOX); tab_browse:selection_color(7)
tab_browse:callback(function() tabs:value(tab1) end)
tab_search=fltk:Fl_Radio_Button(0,0,100,bh-5,"text &search")
tab_search:when(fltk.FL_WHEN_CHANGED)
tab_search:box(fltk.FL_BORDER_BOX); tab_search:selection_color(7)
tab_search:callback(function() tabs:value(tab2); Fl:focus(search_text) end)
fltk:Fl_End()
last_update=fltk:Fl_Box(ww-5,wh-bh-5,5,bh+5)
last_update:align(fltk.FL_ALIGN_LEFT)
last_update:labelsize(12)

win:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tabs:resizable(tab3)
tab1:resizable(tiles)
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(); fill_list()
else
 ask4db=fltk.fl_choice("You have no database for online MyDSL apps.\nDo you want to download it now?","No","Yes",NULL)
 if ask4db==1 then Fl:check(); updatedb() else
 install:deactivate(); tab_search:deactivate(); tab_browse:deactivate()
 info_display_buffer:text("\nThere is no MyDSL database installed.\nClick \"Update Database\" \(Alt+U\) to download it.")
 end
end
Fl:run()

Posted by roberts on April 01 2008,17:35
Ah, you did both, grey out and popup.
Nice job!
Will put in RC2.

Posted by ^thehatsrule^ on April 04 2008,22:09
Quote (mikshaw @ April 01 2008,10:52)
Unfortunately mydsl-load failure (such as the "please mount" error we occasionally see) will bring down the whole gui.  Does anyone know if FLTK has a means of handling errors more gracefully than just shutting down?
Maybe pcall? See < http://www.lua.org/manual/5.1/manual.html#2.7 >

As an alternative: if you're talking about that popup dialog, maybe you could unset the DISPLAY env var.  I haven't looked at the code nor tried it, so I don't know what the exact behaviour will be though.

Posted by mikshaw on April 05 2008,02:26
After posting that I thought it might be Lua rather than FLTK, which would make more sense.  I've noticed a lot of Robert's scripts use "name,err=some_function" , which I assume grabs any error that might occur, but I've never looked into how that works.
Posted by ^thehatsrule^ on April 05 2008,05:09
"retval, err" are just the return codes for some function... it depends on their declarations afaik.  It doesn't do anything except that you could print out the error contents (ie message), assuming that the function error was handled correctly and did not exit prematurely.
Posted by mikshaw on April 05 2008,11:32
Quote
if you're talking about that popup dialog, maybe you could unset the DISPLAY env var
I can't say for sure, and haven't tested yet. The popup still displays, but it only displays when there is an error. There's no way to tell if the error or popup is the trouble unless I changed mountci to show a popup on success as well....haven't tried it yet.

Thanks for the info on the return codes. I guess you pretty much have to know exactly what a function returns before deciding to use it. It took a few confusing mistakes before realizing that many functions return multiple values, and I still have trouble making use of them sometimes.

Quote
Maybe pcall?
This looks promising. Thanks.
It sometimes seems to me that either murgaLua itself does not know how to handle Lua errors, or I should be using something other than Fl:run to start the FLTK display (maybe a main loop or something). Considering interactive Lua doesn't completely shut down on an error, I would think the same behavior could be forced with non-interactive.

Posted by mikshaw on April 22 2008,01:33
This version addresses a couple of bugs found by JLS.

1) Added change directory at line 12
2) Removed (actually just commented for now) line 228

Code Sample
#!/bin/murgaLua
-- MyDSL Browser, 2008 mikshaw

mydsldirfile=io.open("/opt/.mydsl_dir")
if not mydsldirfile then
 fltk.fl_alert("Can't open /opt/.mydsl_dir")
 os.exit(1)
end
mydsl_dir=mydsldirfile:read("*l")
mydsldirfile:close()

if not lfs.chdir(mydsl_dir) then
 fltk.fl_alert("Could not enter directory\n"..mydsl_dir)
 os.exit(2)
end

listfile=mydsl_dir.."/mydslinfo.bz2"
listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/mydslinfo.bz2"
list_sep="@b@B49@u"

function toggle(b)
local count,state,one,two=1
local me,label=b:value(),b:text(b:value())
if string.find(label,"%+%s*") then
 state,one,two="expanded","%+  ","%-  "
else
 state,one,two="collapsed","%-  ","%+  "
end
b:text(me,string.gsub(label,one,two))
while b:text(me+count) and not string.match(b:text(me+count),list_sep) do
 if state=="collapsed" then b:hide(me+count)
 else b:show(me+count)
 end
 count=count+1
end
end

function fill_list()
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
filelist:clear()
header={}
info={}
info_count=0
local current_loc=""
local headers=0
locount=0
for s in string.gmatch(data,"(Location:.-\n)\n") do
 local datestring=string.match(s,"Current:%s*(.-%d)%s")
 if string.find(datestring,"%d%d%D%d%d%D%d%d%d%d") then datestring=string.gsub(datestring,"(%d%d)%D(%d%d)%D(%d%d%d%d)","%3/%1/%2") end
 info_count=info_count+1
 info[info_count]={
 location=string.match(s,"Location:%s*(.-)%s*\n"),
 title=string.match(s,"Title:%s*(.-)%s*\n"),
 date=string.gsub(datestring,"%D","/"),
 text=s
 }
 if current_loc ~= info[info_count].location then
   current_loc=info[info_count].location
   filelist:add(list_sep.."-  "..current_loc)
   headers=headers+1
   header[headers]=filelist:size()
 end
 filelist:add(info[info_count].title)
end
for i,v in ipairs(header) do filelist:value(header[i]); toggle(filelist) end
last_update:label("Database updated "..os.date("%d %b %Y",lfs.attributes(listfile).modification)..", "..info_count.." packages")
end
filelist:value(1)
tab_browse:setonly()
end

function updatedb()
tempfile=os.tmpname()
os.execute(terminal.." -title \"Database Update\" -e wget "..listurl.." -O "..tempfile)
if os.execute("bzip2 -t "..tempfile) == 0 then
 os.execute("mv "..tempfile.." "..listfile)
 install:activate()
 tab_search:activate()
 tab_browse:activate()
 info_display_buffer:text("\nDatabase updated successfully.")
 fill_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
filelist:take_focus()
end

function do_search()
 if search_text:value() ~= "" and info_count and info_count>0 then
   results:clear()
   results:add("@B49@b@uPackage Name\t@B49@b@uLocation\t@B49@b@uRelease Date")
   for i=1,info_count do
     if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,1) then
       results:add(info[i].title.."\t"..info[i].location.."\t"..info[i].date)
     end
   end
   results:insert(1,"@B49@."..tostring(results:size()-1).." search results")
 end
end

function pick_search_result()
if results:value() > 2 then
 get_loc=string.match(results:text(results:value()),"\t(.*)\t")
 get_fname=string.match(results:text(results:value()),"(.-)\t")
 local myloc=1
 while myloc<=filelist:size() do
   if not string.find(filelist:text(myloc),"  "..get_loc,1,1) then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 if string.find(filelist:text(myloc),list_sep.."+  ",1,1) then toggle(filelist) end
 while myloc<=filelist:size() do
   if filelist:text(myloc) ~= get_fname then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 for i=1,info_count do
   if info[i].title == get_fname and info[i].location == get_loc then
     info_display_buffer:text(info[i].text)
     tab_browse:setonly()
     myfile=info[i]
     break
   end
 end
 tabs:value(tab1)
end
end

function list_cb()
if filelist:value()>0 then
 if Fl:event() == fltk.FL_RELEASE or Fl:event_key()==fltk.FL_Enter then
   if string.find(filelist:text(filelist:value()),list_sep) then
     toggle(filelist)
   else
     local myloc=filelist:value()
     while myloc > 0 do
       myloc=myloc-1
       if string.find(filelist:text(myloc),list_sep) then
         myloc=string.gsub(filelist:text(myloc),".-%s%s","")
         break
       end
     end
     for i=1,info_count do
       if info[i].title == filelist:text(filelist:value()) and info[i].location == myloc then
         info_display_buffer:text(info[i].text)
         myfile=info[i]
         tab_browse:setonly()
         break
       end
     end
   end
 end
end
end

function toggle_list()
if listw > 0 then
 oldw=listw
 tiles:position(listw+5,nil,6,nil)
 listw=0
 info_display:take_focus()
else
 tiles:position(6,nil,oldw+5,nil)
 listw=oldw
 filelist:take_focus()
end
end

terminal="aterm +tr -bg white -fg black -geometry 80x4"
ww=560; wh=380; bh=30; bw=(ww-10)/3
win=fltk:Fl_Window(ww,wh,"MyDSL Browser")

tabs=fltk:Fl_Wizard(0,0,ww,wh-bh-5)
tab1=fltk:Fl_Group(0,0,ww,wh-bh-5,"browse")

tiles=fltk:Fl_Tile(5,5+bh,ww-10,wh-(15+bh*2))
filelist=fltk:Fl_Hold_Browser(5,5+bh,1,wh-(15+bh*2))
filelist:callback(list_cb)

info_display=fltk:Fl_Text_Display(6,5+bh,ww-11,wh-(15+bh*2))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)
fltk:Fl_End()

tiles:position(6,nil,ww/3,nil)
tiles:callback(function() listw=filelist:w() end)
listw=filelist:w()

prevheader=fltk:Fl_Button(ww,wh,bh,bh,"&[")
prevheader:callback(
function()
for i=table.getn(header),1,-1 do
 if header[i] < filelist:value() then filelist:value(header[i]); break end
end
filelist:take_focus()
end)
nextheader=fltk:Fl_Button(ww,wh,bh,bh,"&]")
nextheader:callback(
function()
for i=1,table.getn(header) do
 if header[i] > filelist:value() then filelist:value(header[i]); break end
end
filelist:take_focus()
end)
enter=fltk:Fl_Return_Button(ww,wh,10,10)
enter:callback(function() filelist:do_callback() end)
togglist=fltk:Fl_Button(ww,wh,10,10,"&t")
togglist:callback(toggle_list)

b_updatedb=fltk:Fl_Button(5,5,bw,bh,"&Update Database")
b_updatedb:callback(updatedb)
install=fltk:Fl_Button(bw+5,5,bw,bh,"&Install Selected Package")
install:callback(
function()
if myfile then
 mydslload=os.execute("mydsl-load "..myfile.title.." "..myfile.location)
--  if mydslload~=0 then fltk.fl_alert(myfile.title..":\nDownload or Checksum error!") end
end
filelist:take_focus()
end)
install_local=fltk:Fl_Button(bw*2+5,5,bw,bh,"Install &Local Package  @DnArrow")
install_local:callback(
function()
local filename=fltk.fl_file_chooser("Browse for a MyDSL file","Mydsl files (*.{dsl,tar.gz,uci,unc})",mydsl_dir,nil)
if filename then os.execute("mydsl-load "..filename) end
end)
fltk:Fl_End()

tab2=fltk:Fl_Group(0,0,ww,wh-bh-5,"search text")
search_text=fltk:Fl_Input(bh*3+5,5,ww-10-bh*3,bh,"search text ")
search_text:when(fltk.FL_WHEN_ENTER_KEY)
search_text:callback(do_search)

results=fltk:Fl_Hold_Browser(5,5+bh,ww-10,wh-(15+bh*2))
results:column_widths({results:w()/2,150,0})
results:callback( function() if results:value() > 1 and Fl:event()==fltk.FL_RELEASE then pick_search_result() end end)
results_cb=fltk:Fl_Return_Button(ww,wh,bh,bh)
results_cb:callback(pick_search_result)
fltk:Fl_End()
fltk:Fl_End()

tabbuttons=fltk:Fl_Pack(5,wh-bh,200,bh-5)
tabbuttons:type(fltk.FL_HORIZONTAL)
tabbuttons:spacing(5)
tab_browse=fltk:Fl_Radio_Button(0,0,100,bh-5,"&browse")
tab_browse:when(fltk.FL_WHEN_CHANGED)
tab_browse:box(fltk.FL_BORDER_BOX); tab_browse:selection_color(7)
tab_browse:callback(function() tabs:value(tab1) end)
tab_search=fltk:Fl_Radio_Button(0,0,100,bh-5,"text &search")
tab_search:when(fltk.FL_WHEN_CHANGED)
tab_search:box(fltk.FL_BORDER_BOX); tab_search:selection_color(7)
tab_search:callback(function() tabs:value(tab2); Fl:focus(search_text) end)
fltk:Fl_End()
last_update=fltk:Fl_Box(ww-5,wh-bh-5,5,bh+5)
last_update:align(fltk.FL_ALIGN_LEFT)
last_update:labelsize(12)

win:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tabs:resizable(tab3)
tab1:resizable(tiles)
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(); fill_list()
else
 ask4db=fltk.fl_choice("You have no database for online MyDSL apps.\nDo you want to download it now?","No","Yes",NULL)
 if ask4db==1 then Fl:check(); updatedb() else
 install:deactivate(); tab_search:deactivate(); tab_browse:deactivate()
 info_display_buffer:text("\nThere is no MyDSL database installed.\nClick \"Update Database\" \(Alt+U\) to download it.")
 end
end
Fl:run()

Posted by roberts on April 22 2008,03:55
Thanks. I am including it in final 4.3 image.
You just made the cutoff.

Posted by mikshaw on April 22 2008,12:47
=o)
Posted by Juanito on April 24 2008,17:40
Should this work in dsl-3.4.11? I just tried and got:
Code Sample
$ /opt/mydsl_browser.lua
/bin/murgaLua: /opt/mydsl_browser.lua:246: attempt to call method 'column_widths' (a nil value)
stack traceback:
       /opt/mydsl_browser.lua:246: in main chunk
       [C]: ?

Posted by roberts on April 24 2008,18:28
Several releases of murgaLua have transpired since v.3.4.11.
With each release of murgaLua new features have become available.
Likely you can grab the murgaLua binary from 4.3 to try in 3.4.11.

Posted by Juanito on April 24 2008,18:40
That worked fine - thanks :)
Posted by mikshaw on April 24 2008,23:51
column_widths was added to murgaLua with version 0.6.4
It provided a much needed way to split a browser into clean columns.

In previous versions of the mydsl browser I had a check for this feature before using it, but since it was added to DSL the check was removed to reduce the file size.

Posted by mikshaw on April 30 2008,16:06
The following version is for testing purposes only, and should not be considered stable.

Changes include adding columns for application version and package size in the search results, some tweaks to prevent crashes if the database somehow gets corrupted to the point where one or more records cannot be found, and a button to download a package without installing it.  Also removed the mydsl_dir string from certain places where it is no longer needed now that lfs.chdir is being used.

The download does not yet acquire the md5 and info files.  The Busybox version of wget does not support multiple files or a file list, and I haven't decided the best course of action to retrieve all three files.  Is it possible to run a string of commands with "aterm -e"?  Also still need to add a check for a successful download or installation.

Question: Is the file list being mirrored, and if so can that URL be changed to use the user's chosen mirror? Maybe the fact that it is updated often would mean it's best to stick with < ftp://ibiblio.org >

Code Sample
#!/bin/murgaLua
-- MyDSL Browser, 2008 mikshaw

-- TODO: check for /opt/mydsl.installed/* or for downloaded file

dofile("/etc/init.d/functions5.lua")

mydsldirfile=io.open("/opt/.mydsl_dir")
if not mydsldirfile then
 fltk.fl_alert("Can't open /opt/.mydsl_dir")
 os.exit(1)
end
mydsl_dir=mydsldirfile:read("*l")
mydsldirfile:close()
if not lfs.chdir(mydsl_dir) then
 fltk.fl_alert("Could not enter directory\n"..mydsl_dir)
 os.exit(2)
end
listfile="mydslinfo.bz2"
listurl="ftp://ibiblio.org/pub/Linux/distributions/damnsmall/mydsl/mydslinfo.bz2"
list_sep="@b@B49@u"
terminal="aterm +tr -bg white -fg black -geometry 80x6"
mirror=getOption("/opt/.dslrc","Protocol").."://"..getOption("/opt/.dslrc","Mirror").."/mydsl/"

function toggle(b)
local count,state,one,two=1
local me,label=b:value(),b:text(b:value())
if string.find(label,"%+%s*") then
 state,one,two="expanded","%+  ","%-  "
else
 state,one,two="collapsed","%-  ","%+  "
end
b:text(me,string.gsub(label,one,two))
while b:text(me+count) and not string.match(b:text(me+count),list_sep) do
 if state=="collapsed" then b:hide(me+count)
 else b:show(me+count)
 end
 count=count+1
end
end

function fill_list()
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
 filelist:clear()
 header={}
 info={}
 info_count=0
 local current_loc=""
 local headers=0
 locount=0
 for s in string.gmatch(data,"(Location:.-\n)\n") do
   local datestring=string.match(s,"Current:%s*(.-%d)%s")
   if not datestring then datestring="-"
   elseif string.find(datestring,"%d%d%D%d%d%D%d%d%d%d") then
       datestring=string.gsub(datestring,"(%d%d)%D(%d%d)%D(%d%d%d%d)","%3/%1/%2")
   end
   local location=string.match(s,"Location:%s*(.-)%s*\n")
   local title=string.match(s,"Title:%s*(.-)%s*\n")
   local filesize=string.match(s,"Filesize:%s*(.-)%s")
   local date=string.gsub(datestring,"%D","/")
   local version=string.match(s,"Version:%s*(.-)%s*\n")
   local text=s
   info_count=info_count+1
   info[info_count]={
   location=location or "-",
   title=title or "-",
   filesize=filesize or "-",
   date=date or "-",
   version=version or "-",
   text=text or "-"
   }
   if current_loc ~= info[info_count].location then
     current_loc=info[info_count].location
     filelist:add(list_sep.."-  "..current_loc)
     headers=headers+1
     header[headers]=filelist:size()
   end
   filelist:add(info[info_count].title)
 end
 for i,v in ipairs(header) do filelist:value(header[i]); toggle(filelist) end
 last_update:label("Database updated "..
   os.date("%d %b %Y",lfs.attributes(listfile).modification)..
   ", "..info_count.." packages")
end
filelist:value(1)
tab_browse:setonly()
end

function updatedb()
tempfile=os.tmpname()
os.execute(terminal.." -title \"Database Update\" -e wget "..listurl.." -O "..tempfile)
if os.execute("bzip2 -t "..tempfile) == 0 then
 os.execute("mv "..tempfile.." "..listfile)
 install:activate()
 tab_search:activate()
 tab_browse:activate()
 info_display_buffer:text("\nDatabase updated successfully.")
 fill_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
filelist:take_focus()
end

function do_search()
 if search_text:value() ~= "" and info_count and info_count>0 then
   results:clear()
   results:add("@B49@b@uPackage Name\t@B49@b@uLocation\t@B49@b@uFile Size\t@B49@b@uRelease Date\t@B49@b@uVersion")
   for i=1,info_count do
     if string.find(string.lower(info[i].text),string.lower(search_text:value()),1,1) then
       results:add(info[i].title.."\t"..info[i].location.."\t"..info[i].filesize.."\t"..info[i].date.."\t"..info[i].version)
     end
   end
   results:insert(1,"@B49@."..tostring(results:size()-1).." search results")
 end
end

function pick_search_result()
if results:value() > 2 then
 get_loc=string.match(results:text(results:value()),"\t(.-)\t")
 get_fname=string.match(results:text(results:value()),"(.-)\t")
 local myloc=1
 while myloc<=filelist:size() do
   if not string.find(filelist:text(myloc),"  "..get_loc,1,1) then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 if string.find(filelist:text(myloc),list_sep.."+  ",1,1) then toggle(filelist) end
 while myloc<=filelist:size() do
   if filelist:text(myloc) ~= get_fname then
     myloc=myloc+1
   else filelist:value(myloc)
     break
   end
 end
 for i=1,info_count do
   if info[i].title == get_fname and info[i].location == get_loc then
     info_display_buffer:text(info[i].text)
     tab_browse:setonly()
     myfile=info[i]
     break
   end
 end
 tabs:value(tab1)
end
end

function list_cb()
if filelist:value()>0 then
 if Fl:event() == fltk.FL_RELEASE or Fl:event_key()==fltk.FL_Enter then
   if string.find(filelist:text(filelist:value()),list_sep) then
     toggle(filelist)
   else
     local myloc=filelist:value()
     while myloc > 0 do
       myloc=myloc-1
       if string.find(filelist:text(myloc),list_sep) then
         myloc=string.gsub(filelist:text(myloc),".-%s%s","")
         break
       end
     end
     for i=1,info_count do
       if info[i].title == filelist:text(filelist:value()) and info[i].location == myloc then
         info_display_buffer:text(info[i].text)
         myfile=info[i]
         tab_browse:setonly()
         break
       end
     end
   end
 end
end
end

function toggle_list()
if listw > 0 then
 oldw=listw
 tiles:position(listw+5,nil,6,nil)
 listw=0
 info_display:take_focus()
else
 tiles:position(6,nil,oldw+5,nil)
 listw=oldw
 filelist:take_focus()
end
end

function instpkg()
if myfile then
 mydslload=os.execute("mydsl-load "..myfile.title.." "..myfile.location)
--  if mydslload~=0 then fltk.fl_alert(myfile.title..":\nDownload or Checksum error!") end
end
filelist:take_focus()
end

function instlocal()
local filename=fltk.fl_file_chooser("Browse for a MyDSL file","Mydsl files (*.{dsl,tar.gz,uci,unc})",nil,nil)
if filename then os.execute("mydsl-load "..filename) end
end

function dlonly()
if myfile then
 local dirname=fltk.fl_dir_chooser("Save to...",nil,nil)
 if dirname and fltk.fl_filename_isdir(dirname)~=0 then
   Fl:check()
   os.execute(terminal.." -title \"Downloading "..myfile.title.."\" -e wget "..
       mirror..myfile.location.."/"..myfile.title.." -P "..dirname)
 else print("no") end
end
end

ww=570; wh=380; bh=30; bw=(ww-10)/4
win=fltk:Fl_Window(ww,wh,"MyDSL Browser")

tabs=fltk:Fl_Wizard(0,0,ww,wh-bh-5)
tab1=fltk:Fl_Group(0,0,ww,wh-bh-5,"browse")

tiles=fltk:Fl_Tile(5,5+bh,ww-10,wh-(15+bh*2))
filelist=fltk:Fl_Hold_Browser(5,5+bh,1,wh-(15+bh*2))
filelist:callback(list_cb)

info_display=fltk:Fl_Text_Display(6,5+bh,ww-11,wh-(15+bh*2))
info_display:textfont(fltk.FL_SCREEN)
info_display:textsize(12)
info_display_buffer=fltk:Fl_Text_Buffer()
info_display:buffer(info_display_buffer)
fltk:Fl_End()

tiles:position(6,nil,ww/3,nil)
tiles:callback(function() listw=filelist:w() end)
listw=filelist:w()

prevheader=fltk:Fl_Button(ww,wh,bh,bh,"&[")
prevheader:callback(
function()
for i=table.getn(header),1,-1 do
 if header[i] < filelist:value() then filelist:value(header[i]); break end
end
filelist:take_focus()
end)
nextheader=fltk:Fl_Button(ww,wh,bh,bh,"&]")
nextheader:callback(
function()
for i=1,table.getn(header) do
 if header[i] > filelist:value() then filelist:value(header[i]); break end
end
filelist:take_focus()
end)
enter=fltk:Fl_Return_Button(ww,wh,10,10)
enter:callback(function() filelist:do_callback() end)
togglist=fltk:Fl_Button(ww,wh,10,10,"&t")
togglist:callback(toggle_list)

b_updatedb=fltk:Fl_Button(5,5,bw,bh,"&Update Database")
b_updatedb:callback(updatedb)
install=fltk:Fl_Button(bw+5,5,bw,bh,"&Install Selected")
install:callback(instpkg)
download=fltk:Fl_Button(bw*2+5,5,bw,bh,"&Download only")
download:callback(dlonly)
install_local=fltk:Fl_Button(bw*3+5,5,bw,bh,"Install &Local  @DnArrow")
install_local:callback(instlocal)
fltk:Fl_End()

tab2=fltk:Fl_Group(0,0,ww,wh-bh-5,"search text")
search_text=fltk:Fl_Input(bh*3+5,5,ww-10-bh*3,bh,"search text ")
search_text:when(fltk.FL_WHEN_ENTER_KEY)
search_text:callback(do_search)

results=fltk:Fl_Hold_Browser(5,5+bh,ww-10,wh-(15+bh*2))
results:column_widths({results:w()/2,120,120,120,0})
results:callback( function()
 if results:value() > 1 and Fl:event()==fltk.FL_RELEASE then pick_search_result() end
end)
results_cb=fltk:Fl_Return_Button(ww,wh,bh,bh)
results_cb:callback(pick_search_result)
fltk:Fl_End()
fltk:Fl_End()

tabbuttons=fltk:Fl_Pack(5,wh-bh,200,bh-5)
tabbuttons:type(fltk.FL_HORIZONTAL)
tabbuttons:spacing(5)
tab_browse=fltk:Fl_Radio_Button(0,0,100,bh-5,"&browse")
tab_browse:when(fltk.FL_WHEN_CHANGED)
tab_browse:box(fltk.FL_BORDER_BOX); tab_browse:selection_color(7)
tab_browse:callback(function() tabs:value(tab1) end)
tab_search=fltk:Fl_Radio_Button(0,0,100,bh-5,"text &search")
tab_search:when(fltk.FL_WHEN_CHANGED)
tab_search:box(fltk.FL_BORDER_BOX); tab_search:selection_color(7)
tab_search:callback(function() tabs:value(tab2); Fl:focus(search_text) end)
fltk:Fl_End()
last_update=fltk:Fl_Box(ww-5,wh-bh-5,5,bh+5)
last_update:align(fltk.FL_ALIGN_LEFT)
last_update:labelsize(12)

win:resizable(tabs)
tabs:resizable(tab1)
tabs:resizable(tab2)
tabs:resizable(tab3)
tab1:resizable(tiles)
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(); fill_list()
else
 ask4db=fltk.fl_choice("You have no database for online MyDSL apps.\nDo you want to download it now?","No","Yes",NULL)
 if ask4db==1 then Fl:check(); updatedb() else
 install:deactivate(); tab_search:deactivate(); tab_browse:deactivate()
 info_display_buffer:text("\nThere is no MyDSL database installed.\nClick \"Update Database\" \(Alt+U\) to download it.")
 end
end
Fl:run()

Posted by lucky13 on April 30 2008,16:47
It executes one command. You can always run
aterm -e somescript

where somescript contains the commands you want executed.

Posted by ^thehatsrule^ on April 30 2008,17:36
You could always pass them to the shell to execute, i.e. aterm -e sh -c "echo 1 && echo 2; echo $? > retval.txt; read"

I don't think you can use the return code from os.execute since it would get it from aterm - not the commands you specified.
It's not elegant, but it's an alternative that I think would work for now :P

afaik most(all?) of the mirrors do regular rsync updates to ibiblio... so it should be OK.

Posted by mikshaw on April 30 2008,17:52
Quote
You can always run
aterm -e somescript
I was thinking about that, although I need to decide the best way to create the script (probably use the existing tempfile). I don't really want it to be a separate script file.

Quote
You could always pass them to the shell to execute, i.e. aterm -e sh -c "echo 1 && echo 2; echo $? > retval.txt; read"
That looks like a variation of writing to a temp file from lua and then executing the temp file...except using sh instead of lua. Is that right?

Quote
I don't think you can use the return code from os.execute since it would get it from aterm - not the commands you specified.
Yeah, I ran into that issue when trying to get the return value from mydsl-load, which was causing an error dialog even on success. I plan to do checks on the files themselves, after the os.execute has run, in order to give the user a visual clue of the success of the download/installation. I just haven't bothered with it yet.

Quote
afaik most(all?) of the mirrors do regular rsync updates to ibiblio... so it should be OK.
I was hoping that would be the case. Since the script now uses /opt/.dslrc for the "download only" fuction, it might make sense to use that mirror for the database as well.

Thanks for the input guys.  It's always appreciated, and usually helpful.

Posted by ^thehatsrule^ on April 30 2008,18:20
Quote
That looks like a variation of writing to a temp file from lua and then executing the temp file...except using sh instead of lua. Is that right?
Not quite... it does something like
1. run the first command "echo 1"
2. if the last command was successful, run the 2nd command "echo 2"
3. save the return code from the previous statement "echo 1 && echo 2" to a file called retval.txt
4. "read" does nothing - it's just to keep the window open

Quote
Yeah, I ran into that issue when trying to get the return value from mydsl-load, which was causing an error dialog even on success. I plan to do checks on the files themselves, after the os.execute has run, in order to give the user a visual clue of the success of the download/installation. I just haven't bothered with it yet.
Did you try changing the DISPLAY variable like what I suggested in the other post? [ah, not yet]  Just tested it and it seems to work under 3.x.

Posted by mikshaw on May 01 2008,03:12
I don't see why you'd want to unset the DISPLAY variable.  The error dialog that mydsl-load provides is a desired behavior.  The error dialog I was getting was one that was part of this script.  It was not working properly, however, because of the issue you mentioned...applications running in an X terminal do not return to the shell that started the terminal.

Your idea of redirecting the output of the last command is a good one, but I'm not sure if it will work in this case. It might at least require unsetting the DISPLAY variable in order to prevent a second terminal, meaning  I'd still need to have my own error dialog, which is the exact thing I'm looking to prevent. Since that function is already in place in DSL, I'd prefer to use it rather than mimic it.  I'll have to run some tests.  Any way I look at it, though, it seems like there will be some redundancy...either in presenting the dialog or doing the md5sum test myself.

Although, with the read command + unsetting DISPLAY, the user will be presented with the success or failure message in that term, without the need for a FLTK message or md5sum check.  Interesting idea....I wish I hadn't typed all that out before realizing what you were getting at....

Posted by Juanito on May 01 2008,15:01
'tried your latest version in dsl-3.4.11 using murgaLua from dsl-4.3 - works fine for me.

I like the option to download without loading

Posted by mikshaw on May 02 2008,13:28
^thehatsrule^: That's brilliant!  It took a few tries to get the syntax right, but it works using unset DISPLAY and sh -c

If you weren't so ugly I'd give you a hug =o)

Code Sample
function dlonly()
if myfile then
 local dirname=fltk.fl_dir_chooser("Save to...",nil,nil)
 if dirname and fltk.fl_filename_isdir(dirname)~=0 then
   Fl:check()
   os.execute(terminal..[[ -title "Downloading ]]..
   myfile.title..[[" -e sh -c "unset DISPLAY && cd ]]..dirname..
   [[ &&  wget ]]..mirror..myfile.location..[[/]]..myfile.title..
   [[{.info,.md5.txt,}]]..[[ && md5sum -c ]]..myfile.title..
   [[.md5.txt && echo Press \"Enter\" to continue && read"]])
 end
end
end

Still need to test some more, and probably reconsider using && for every command (want to keep the term open regardless of failure)

Powered by Ikonboard 3.1.2a
Ikonboard © 2001 Jarvis Entertainment Group, Inc.