Author Topic: MTSB  (Read 14224 times)

Support

  • Administrator
  • *****
  • Posts: 1
    • View Profile
MTSB
« on: December 29, 2010, 08:02:57 pm »
ScriptBasic provides in it's distribution a variation of the language called sbhttpd. This example use of ScriptBasic shows how to assemble a multi-threaded webserver with in-memory session support and running as a service/daemon. I have used the sbhttpd server as a proxy behind Apache for years for my web based applications. I thought I would expand on the concept and include desktop applications to the CGI client/server realm. This concept can be used with any server side scripting language and a client with cURL like functionality.

The following posts were from the All Basic site I'm migrating here.

Support

  • Administrator
  • *****
  • Posts: 1
    • View Profile
Re: MTSB
« Reply #1 on: December 29, 2010, 08:04:28 pm »
I thought I would start off with the browser (HTML) version first to show how the flow of a CGI client/server application might look. Keep in mind there is no formating (CSS) or validation to keep this simple and easy to read.

webcustomer.sb
Code: [Select]
' Program: webcustomer.sb
' Version: 1.0
'    Date: 2010-12-27
'      By: JRS

INCLUDE cgi.bas
INCLUDE mt.bas
INCLUDE mysql.bas

dbh = mysql::RealConnect("localhost","root","xxx","sbtest")

OPTION cgi$Method cgi::GET or cgi::POST

session_id = cgi::Cookie("webcust")

New_Session:

IF session_id = undef THEN
  session_id = mt::NewSessionId()
  mt::SetSessionId(session_id)
ELSE
  IF mt::CheckSessionId(session_id) = False THEN
    session_id = undef
    GOTO New_Session
  END IF
  mt::SetSessionId(session_id)
END IF

IF cgi::RequestMethod() = "GET" THEN GOTO Enter_Customer
cust_id = cgi::PostParam("cust_id")
cust_name = cgi::PostParam("cust_name")
cust_addr = cgi::PostParam("cust_addr")
cust_city = cgi::PostParam("cust_city")
cust_state = cgi::PostParam("cust_state")
cust_zip = cgi::PostParam("cust_zip")
cust_phone = cgi::PostParam("cust_phone")

SQL = "INSERT INTO customer (cust_id, cust_name, cust_addr, cust_city, cust_state, cust_zip, cust_phone) VALUES ('" & cust_id & "', '" & cust_name & "', '" & cust_addr & "', '" & cust_city & "', '" & cust_state & "', '" & cust_zip & "', '" & cust_phone & "')"  
mysql::query(dbh, SQL)

SQL = "SELECT * FROM customer"
mysql::query(dbh, SQL)

cgi::Header(200, "text/html")
cgi::SetCookie("webcust", session_id)
cgi::FinishHeader()
PRINT """
<html>
<head>
<title>Customer Master File list</title>
</head>
<body>
<table border="1">
  <tr>
    <th>ID</th>
    <th>Name</th>
    <th>Address</th>
    <th>City</th>
    <th>State</th>
    <th>Zip</th>
    <th>Phone</th>
  </tr>
"""
WHILE mysql::FetchHash(dbh, dbcol)
  PRINT """
  <tr>
    <td>""" & dbcol{"cust_id"} & """</td>
    <td>""" & dbcol{"cust_name"} & """</td>
    <td>""" & dbcol{"cust_addr"} & """</td>
    <td>""" & dbcol{"cust_city"} & """</td>
    <td>""" & dbcol{"cust_state"} & """</td>
    <td>""" & dbcol{"cust_zip"} & """</td>
    <td>""" & dbcol{"cust_phone"} & """</td>
  </tr>
"""
WEND
PRINT """
  <tr>
    <td colspan=7 align="left"><br><input type="button" value="Add New Customer" onClick="parent.location='/mtsb/webcustomer.sb'"></td>
  </tr>
</table>
</body>
</html>
"""
END

Enter_Customer:

cgi::Header(200, "text/html")
cgi::SetCookie("webcust", session_id)
cgi::FinishHeader()

PRINT """
<html>
<head>
<title>Customer Master File Maintenance</title>
</head>
<body>
<form name="custform" method="POST" action="/mtsb/webcustomer.sb">
<table border="0">
  <tr>
    <td align="right">ID</td>
    <td><input type="text" name="cust_id" size="5"></td>
  </tr>
  <tr>
    <td align="right">Name</td>
    <td><input type="text" name="cust_name" size="30"></td>
  </tr>
  <tr>
    <td align="right">Address</td>
    <td><input type="text" name="cust_addr" size="30"></td>
   </tr>
  <tr>
    <td align="right">City</td>
    <td><input type="text" name="cust_city" size="20"></td>
  </tr>
  <tr>
    <td align="right">State</td>
    <td><input type="text" name="cust_state" size="5"></td>
  </tr>
  <tr>
    <td align="right">Zip</td>
    <td><input type="text" name="cust_zip" size="5"></td>
  </tr>
  <tr>
    <td align="right">Phone</td>
    <td><input type="text" name="cust_phone" size="20"></td>
  </tr>
  <tr>
    <td colspan=2 align="center"><br><input type="submit" value="Add Customer"></td>
  </tr>
</table>
</form>
</body>
</html>
"""

MySQL - customer table
Code: [Select]
CREATE TABLE `sbtest`.`customer` (
`cust_id` VARCHAR( 5 ) NOT NULL ,
`cust_name` VARCHAR( 30 ) NULL ,
`cust_addr` VARCHAR( 30 ) NULL ,
`cust_city` VARCHAR( 20 ) NULL ,
`cust_state` VARCHAR( 5 ) NULL ,
`cust_zip` VARCHAR( 5 ) NULL ,
`cust_phone` VARCHAR( 20 ) NULL ,
PRIMARY KEY ( `cust_id` )
) ENGINE = MYISAM ;
« Last Edit: December 29, 2010, 08:06:12 pm by support »

Support

  • Administrator
  • *****
  • Posts: 1
    • View Profile
Re: MTSB
« Reply #2 on: December 29, 2010, 08:12:52 pm »
I webified one of the old All Basic code challenge projects that is based on Gtk and MySQL as the DB. The real benefit as I see it comes to light when you start running detached updates and reports on the server in a multi-threaded task.


csgtkcustomer.sb
Code: [Select]
' All Basic address book challenge - 9/15/2008
'
' Basic Language: ScriptBasic 2.1 (Windows / Linux)
'
' Authors: Peter van Eerten - www.gtk-server.org
'          John Spikowski - www.scriptbasic.org

' GTK-server extension module
INCLUDE gtk.bas

' cURL extension module
INCLUDE curl.bas

GLOBAL CONST TAB = "\t"
GLOBAL CONST NL = "\n"

ch = curl::init()

SUB Fill_Data

' clear the list
gtk_list_store_clear(List_Store)

curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/gtkdbweb.sb")
curl::option(ch,"COOKIEJAR","/home/jrs/SB/test/cookie.txt")
db_response = curl::perform(ch)

WHILE LEN(db_response)
  eor = INSTR(db_response,NL)
  this_row = LEFT(db_response, eor - 1)
  SPLIT this_row BY TAB TO dbrow{"cust_id"},dbrow{"cust_name"},dbrow{"cust_addr"},dbrow{"cust_city"},dbrow{"cust_state"},dbrow{"cust_zip"},dbrow{"cust_phone"}
  gtk_list_store_append(List_Store, List_Iter)
  gtk_list_store_set(List_Store, List_Iter, 0, dbrow{"cust_id"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 1, dbrow{"cust_name"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 2, dbrow{"cust_addr"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 3, dbrow{"cust_city"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 4, dbrow{"cust_state"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 5, dbrow{"cust_zip"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 6, dbrow{"cust_phone"}, -1)
  db_response = MID(db_response, eor + 1)
WEND

END SUB

SUB Add_Edit_Data

' determine which mode we are
IF Add_Edit_Mode = 1 THEN
    gtk_list_store_append(List_Store, List_Iter)
END IF

' Get the etries, currently no checking
info[0] = gtk_entry_get_text(EntryID)
info[1] = gtk_entry_get_text(EntryName)
info[2] = gtk_entry_get_text(EntryAddress)
info[3] = gtk_entry_get_text(EntryCity)
info[4] = gtk_entry_get_text(EntryState)
info[5] = gtk_entry_get_text(EntryZip)
info[6] = gtk_entry_get_text(EntryPhone)

' Add mode, store the info into the list widget
FOR i = 0 TO 6
    gtk_list_store_set(List_Store, List_Iter, i, info[i], -1)
NEXT i

' Delete the entries
gtk_editable_delete_text(EntryID, 0, -1)
gtk_editable_delete_text(EntryName, 0, -1)
gtk_editable_delete_text(EntryAddress, 0, -1)
gtk_editable_delete_text(EntryCity, 0, -1)
gtk_editable_delete_text(EntryState, 0, -1)
gtk_editable_delete_text(EntryZip, 0, -1)
gtk_editable_delete_text(EntryPhone, 0, -1)

post_str = "cust_id=" & curl::escape(info[0]) & "&cust_name=" & curl::escape(info[1]) & "&cust_addr=" & curl::escape(info[2]) & "&cust_city=" & curl::escape(info[3]) & "&cust_state=" & curl::escape(info[4]) & "&cust_zip=" & curl::escape(info[5]) & "&cust_phone=" & curl::escape(info[6]) & "&maint_flag=" & curl::escape(Add_Edit_Mode)

curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/gtkdbweb.sb")
curl::option(ch,"COOKIE","webcust")
curl::option(ch,"POST")
curl::option(ch,"POSTFIELDS",post_str)
txt = curl::perform(ch)

END SUB

SUB Del_Data

' Check if a row is selected
IF gtk_tree_selection_get_selected(Tree_Sel, "NULL", List_Iter) = "1" THEN
   gtk_list_store_remove(List_Store, List_Iter)
   post_str = "cust_id=" & curl::escape(info[0]) & "&maint_flag=" & "3"
   curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/gtkdbweb.sb")
   curl::option(ch,"COOKIE","webcust")
   curl::option(ch,"POST")
   curl::option(ch,"POSTFIELDS",post_str)
   txt = curl::perform(ch)
ELSE
    gtk_widget_show_all(Error_Msg)
END IF

END SUB

' MAIN program

' Optionally enable GTK logging
gtk_server_cfg("-log=log.txt")

' Get GLADE definition
xml = glade_xml_new("form.glade")
glade_xml_signal_autoconnect(xml)

' Get main window ID and connect signal
Main_Window = glade_xml_get_widget(xml, "Main_Window")
gtk_server_connect(Main_Window, "delete-event", "Main_Window")

' Get button IDs and connect signals
Add_Button = glade_xml_get_widget(xml, "Add_Button")
gtk_server_connect(Add_Button, "clicked", "Add_Button")
Del_Button = glade_xml_get_widget(xml, "Del_Button")
gtk_server_connect(Del_Button, "clicked", "Del_Button")
Edit_Button = glade_xml_get_widget(xml, "Edit_Button")
gtk_server_connect(Edit_Button, "clicked", "Edit_Button")
Exit_Button = glade_xml_get_widget(xml, "Exit_Button")
gtk_server_connect(Exit_Button, "clicked", "Exit_Button")

' Get scrolled window
Scrolled_Window =  glade_xml_get_widget(xml, "Scrolled_Window")

' Define the list
List_Iter = gtk_server_opaque()
GTK::gtk("gtk_server_redefine gtk_list_store_new NONE WIDGET 8 INT INT INT INT INT INT INT INT")
List_Store = GTK::gtk("gtk_list_store_new 7 64 64 64 64 64 64 64")
List_Choice = gtk_tree_view_new_with_model(List_Store)
GTK::gtk("gtk_server_connect " & List_Choice & " button-press-event " & List_Choice & " 1")
gtk_tree_view_set_headers_visible(List_Choice, 1)
gtk_tree_view_set_headers_clickable(List_Choice, 1)
gtk_tree_view_set_grid_lines(List_Choice, 3)
gtk_tree_sortable_set_sort_column_id(List_Store, 0, 0)
Tree_Sel = gtk_tree_view_get_selection(List_Choice)
gtk_tree_selection_set_mode(Tree_Sel, 2)
Txt_Cell = gtk_cell_renderer_text_new()
List_Column0 = gtk_tree_view_column_new_with_attributes("ID", Txt_Cell, "text", 0, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column0)
gtk_tree_view_column_set_resizable(List_Column0, 1)
gtk_tree_view_column_set_clickable(List_Column0, 1)
List_Column1 = gtk_tree_view_column_new_with_attributes("Name", Txt_Cell, "text", 1, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column1)
gtk_tree_view_column_set_resizable(List_Column1, 1)
List_Column2 = gtk_tree_view_column_new_with_attributes("Address", Txt_Cell, "text", 2, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column2)
gtk_tree_view_column_set_resizable(List_Column2, 1)
List_Column3 = gtk_tree_view_column_new_with_attributes("City", Txt_Cell, "text", 3, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column3)
gtk_tree_view_column_set_resizable(List_Column3, 1)
List_Column4 = gtk_tree_view_column_new_with_attributes("State", Txt_Cell, "text", 4, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column4)
gtk_tree_view_column_set_resizable(List_Column4, 1)
List_Column5 = gtk_tree_view_column_new_with_attributes("ZIP", Txt_Cell, "text", 5, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column5)
gtk_tree_view_column_set_resizable(List_Column5, 1)
List_Column6 = gtk_tree_view_column_new_with_attributes("Phone", Txt_Cell, "text", 6, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column6)
gtk_tree_view_column_set_resizable(List_Column6, 1)
gtk_container_add(Scrolled_Window, List_Choice)
gtk_widget_show_all(Scrolled_Window)

' Add entry window
Entry_Field = glade_xml_get_widget(xml, "Entry_Field")
gtk_server_connect(Entry_Field, "delete-event", "Entry_Field")

' Get button ID and connect signal
Entry_Ok_Button = glade_xml_get_widget(xml, "Entry_Ok_Button")
gtk_server_connect(Entry_Ok_Button, "clicked", "Entry_Ok_Button")
Entry_Can_Button = glade_xml_get_widget(xml, "Entry_Can_Button")
gtk_server_connect(Entry_Can_Button, "clicked", "Entry_Can_Button")

' Get ID's for the textentries
EntryID = glade_xml_get_widget(xml, "EntryID")
EntryName = glade_xml_get_widget(xml, "EntryName")
EntryAddress = glade_xml_get_widget(xml, "EntryAddress")
EntryCity = glade_xml_get_widget(xml, "EntryCity")
EntryState = glade_xml_get_widget(xml, "EntryState")
EntryZip = glade_xml_get_widget(xml, "EntryZip")
EntryPhone = glade_xml_get_widget(xml, "EntryPhone")

' Warning dialog, define here because of a bug in Glade
Warning_Msg = gtk_message_dialog_new(Main_Window, 1, 1, 4, "\nAre you sure to delete this entry?", "''")
gtk_window_set_title(Warning_Msg, "Warning")
gtk_server_connect(Warning_Msg, "delete-event", "Warning_Msg")
gtk_server_connect(Warning_Msg, "response", "Warning_Msg_Response")

' Warning dialog, define here because of a bug in Glade
Error_Msg = gtk_message_dialog_new(Main_Window, 1, 3, 2, "\nSelect an entry first!", "''")
gtk_window_set_title(Error_Msg, "Error!")
gtk_server_connect(Error_Msg, "delete-event", "Error_Msg")

' Fill grid with data
CALL Fill_Data()

' Set sort flag
Sort_Flag = 0

' Set edit or add mode - 1=ADD, 2=EDIT
Add_Edit_Mode = 1

' Mainloop starts here
REPEAT

    ' Get event
    event = gtk_server_callback("wait")

    ' Add entry form
    IF event = "Add_Button" THEN
Add_Edit_Mode = 1
gtk_window_set_title(Entry_Field, "Add entry")
gtk_widget_show_all(Entry_Field)
gtk_widget_grab_focus(EntryID)
    END IF

    ' Edit entry form
    IF event = "Edit_Button" THEN
Add_Edit_Mode = 2
IF gtk_tree_selection_get_selected(Tree_Sel, "NULL", List_Iter) = "0" THEN
   gtk_widget_show_all(Error_Msg)
ELSE
   FOR i = 0 TO 6
info[i] = gtk_tree_model_get(List_Store, List_Iter, i, "''", -1)
   NEXT i

   ' Get the etries, currently no checking
   gtk_entry_set_text(EntryID, MID(info[0], 4))
   gtk_entry_set_text(EntryName, MID(info[1], 4))
   gtk_entry_set_text(EntryAddress, MID(info[2], 4))
   gtk_entry_set_text(EntryCity, MID(info[3], 4))
   gtk_entry_set_text(EntryState, MID(info[4], 4))
   gtk_entry_set_text(EntryZip, MID(info[5], 4))
   gtk_entry_set_text(EntryPhone, MID(info[6], 4))

   gtk_window_set_title(Entry_Field, "Edit entry")
   gtk_widget_show_all(Entry_Field)
   gtk_widget_grab_focus(EntryID)

END IF
    END IF

    ' These are the buttons on the entry form
    IF event = "Entry_Can_Button" OR event = "Entry_Field" THEN
gtk_widget_hide(Entry_Field)

' Delete the entries
gtk_editable_delete_text(EntryID, 0, -1)
gtk_editable_delete_text(EntryName, 0, -1)
gtk_editable_delete_text(EntryAddress, 0, -1)
gtk_editable_delete_text(EntryCity, 0, -1)
gtk_editable_delete_text(EntryState, 0, -1)
gtk_editable_delete_text(EntryZip, 0, -1)
gtk_editable_delete_text(EntryPhone, 0, -1)
    END IF
    IF event = "Entry_Ok_Button" THEN
CALL Add_Edit_Data()
    END IF

    ' Warning dialog
    IF event = "Del_Button" THEN
IF gtk_tree_selection_get_selected(Tree_Sel, "NULL", List_Iter) = "0" THEN
   gtk_widget_show_all(Error_Msg)
ELSE
   gtk_widget_show_all(Warning_Msg)
END IF
    END IF
    IF event = "Warning_Msg" THEN gtk_widget_hide(Warning_Msg)
    IF event = "Warning_Msg_Response" THEN
response = gtk_server_callback_value(1, "INT")
IF response = -8 THEN CALL Del_Data()
gtk_widget_hide(Warning_Msg)
    END IF

    ' Error dialog
    IF event = "Error_Msg" OR event = Error_Msg THEN gtk_widget_hide(Error_Msg)

    ' Sortable on key
    IF event = List_Column0 THEN
Sort_Flag = 1 - Sort_Flag
gtk_tree_sortable_set_sort_column_id(List_Store, 0, Sort_Flag)
    END IF

UNTIL event = "Main_Window" OR event = "Exit_Button"

' Cleanup resources
gtk_server_exit()
curl::finish(ch)

gtkdbweb.sb
Code: [Select]
' Program: gtkdbweb.sb
' Version: 1.0
'    Date: 2010-12-27
'      By: JRS

INCLUDE cgi.bas
INCLUDE mt.bas
INCLUDE mysql.bas

CONST TAB = "\t"
CONST NL = "\n"

dbh = mysql::RealConnect("localhost","root","xxxx","sbtest")

OPTION cgi$Method cgi::GET or cgi::POST

session_id = cgi::Cookie("webcust")

New_Session:

IF session_id = undef THEN
  session_id = mt::NewSessionId()
  mt::SetSessionId(session_id)
ELSE
  IF mt::CheckSessionId(session_id) = False THEN
    session_id = undef
    GOTO New_Session
  END IF
  mt::SetSessionId(session_id)
END IF

IF cgi::RequestMethod() = "POST" THEN GOTO Maintenance

mysql::query(dbh,"SELECT * FROM customer")

cgi::Header(200, "text/html")
cgi::SetCookie("webcust", session_id)
cgi::FinishHeader()

WHILE mysql::FetchHash(dbh, dbrow)
  PRINT dbrow{"cust_id"} & TAB & dbrow{"cust_name"} & TAB & dbrow{"cust_addr"} & TAB & dbrow{"cust_city"} & TAB & dbrow{"cust_state"} & TAB & dbrow{"cust_zip"} & TAB & dbrow{"cust_phone"} & NL
WEND

mysql::Close(dbh)
END

Maintenance:

cust_id = cgi::PostParam("cust_id")
cust_name = cgi::PostParam("cust_name")
cust_addr = cgi::PostParam("cust_addr")
cust_city = cgi::PostParam("cust_city")
cust_state = cgi::PostParam("cust_state")
cust_zip = cgi::PostParam("cust_zip")
cust_phone = cgi::PostParam("cust_phone")
maint_flag = cgi::PostParam("maint_flag")

IF maint_flag = "1" THEN SQL = "INSERT INTO customer (cust_id, cust_name, cust_addr, cust_city, cust_state, cust_zip, cust_phone) VALUES ('" & cust_id & "', '" & cust_name & "', '" & cust_addr & "', '" & cust_city & "', '" & cust_state & "', '" & cust_zip & "', '" & cust_phone & "')"
IF maint_flag = "2" THEN SQL = "UPDATE customer SET cust_id = '" & cust_id & "', cust_name = '" & cust_name & "', cust_addr = '" & cust_addr & "', cust_city = '" & cust_city & "', cust_state = '" & cust_state & "', cust_zip = '" & cust_zip & "', cust_phone = '" & cust_phone & "'"
IF maint_flag = "3" THEN SQL = "DELETE FROM customer WHERE cust_id = " & cust_id
    
mysql::query(dbh, SQL)

cgi::Header(200, "text/html")
cgi::SetCookie("webcust", session_id)
cgi::FinishHeader()

mysql::Close(dbh)
END

Support

  • Administrator
  • *****
  • Posts: 1
    • View Profile
Re: MTSB
« Reply #3 on: December 30, 2010, 02:12:23 am »
Here is an example of running the Bible word count code challenge on the sbhttpd server as side by side threads and returning immediately to my main testfaith.sb program to continue on with local processing.

I have been trying to solve an issue of the server releasing control back to the client and continue running on the server in it's own thread. Peter Verhas suggested I try closing channel zero on the server in my script but SB complained saying 1-512 are the only valid channels numbers. Not willing to give up easy, something I read in the SB docs on CGI methods gave me a clue that I might be able to do a HEAD request and return back to the client leaving the script to continue running till it sees an END.

Success!

testfaith.sb
Code: [Select]
INCLUDE curl.bas

ch = curl::init()

'Start 1st thread
curl::option(ch,"NOBODY")
curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/faith.cgi")
txt = curl::perform(ch)

'Start 2nd thread
curl::option(ch,"NOBODY")
curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/faith2.cgi")
txt = curl::perform(ch)

curl::finish(ch)


faith.cgi
Code: [Select]
INCLUDE cgi.bas

option cgi$Method cgi::Get or cgi::Upload or cgi::Head

cgi::Header(200, "text/html")
cgi::FinishHeader()

fname = "/home/jrs/SB/test/Bible.txt"

start_time = NOW
fsize = FILELEN(fname)
OPEN fname FOR INPUT AS #1
text = INPUT(fsize, #1)
CLOSE #1

strip = "()[]{}|<>/@0123456789*.,;:!#?%$&+=_~\"\\" & CHR(9) & CHR(10) & CHR(13)
FOR i = 1 TO LEN(strip)
  text = REPLACE(text, MID(strip, i, 1), " ")
NEXT i

SPLITA text BY " " TO word_list

OPEN "/home/jrs/SB/test/wc.raw" FOR OUTPUT AS #1
FOR x = 0 TO UBOUND(word_list)
  text_out = TRIM(word_list[x])
  IF LEN(text_out) THEN PRINT #1,LCASE(text_out),"\n"
NEXT x

CLOSE #1

ok = EXECUTE("sort /home/jrs/SB/test/wc.raw -o /home/jrs/SB/test/wc.srt", -1, PID)
OPEN "/home/jrs/SB/test/wc.srt" FOR INPUT AS #2
OPEN "/home/jrs/SB/test/wc.lst" FOR OUTPUT AS #3


last_word = ""
word_count = 0
word_total = 0

Next_Word:

IF EOF(2) THEN GOTO Done
LINE INPUT #2, this_word
this_word = CHOMP(this_word)
word_total += 1
IF last_word = "" THEN last_word = this_word
IF this_word = last_word THEN
  word_count += 1
  GOTO Next_Word
END IF

PRINT #3, last_word & " (" & word_count & ")\n"
last_word = this_word
word_count = 1
GOTO Next_Word  

Done:
PRINTNL #3
PRINT #3, word_total - 1, " words in ", NOW - start_time, " seconds.\n"
CLOSE #2
CLOSE #3

END

Note: faith2.cgi is the same as faith.cgi except I added a 2 to the end of each wc*.xxx work file name. I could have passed a thread count ID but this was only a two thread test so I was lazy.

Results:

Bible - Bible in text format. (4.2 MB)

Word List - All words in the Bible, sorted and the number of times they are used. (what this program does - should work with any text file)

wc.sb - original word count SB program I started from the command line with scriba.  
804895 words in 56 seconds.

faith.cgi - running as a single thread task
804895 words in 56 seconds.

faith.cgi - running along side faith2.cgi
804895 words in 121 seconds.

faith2.cgi - running along side faith.cgi
804895 words in 122 seconds.

As this was going on my testfaith.sb program returned back immediately after starting the program. (could have continued on doing something else or initiating new scripts)
« Last Edit: December 30, 2010, 07:54:22 am by support »

Support

  • Administrator
  • *****
  • Posts: 1
    • View Profile
Re: MTSB
« Reply #4 on: January 01, 2011, 10:56:59 am »
Here is an example of ScriptBasic embeding itself in a thread of itself.

INCLUDE curl.bas

ch = curl::init()

curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/threadembed.sb")
txt = curl::perform(ch)
PRINT txt

curl::finish(ch)
 
PRINT "Status: 200\nContent-Type: text/html\n\n"

DECLARE SUB DLL ALIAS "_idll" LIB "iDLL-Server"
DECLARE SUB DEFINE ALIAS "_idll_define" LIB "iDLL-Server"
DECLARE SUB REQUIRE ALIAS "_idll_require" LIB "iDLL-Server"

REQUIRE "libiDLL.so"
DEFINE "sb_new NONE POINTER 0"
DEFINE "scriba_LoadConfiguration NONE INT 2 POINTER STRING"
DEFINE "scriba_SetFileName NONE INT 2 POINTER STRING"
DEFINE "scriba_Run NONE INT 2 POINTER STRING"
DEFINE "scriba_LoadSourceProgram NONE INT 1 POINTER"
DEFINE "scriba_destroy NONE NONE 1 POINTER"

pProgram = DLL("sb_new")
DLL("scriba_LoadConfiguration " & pProgram & " \"/etc/scriba/basic.conf\"")
DLL("scriba_SetFileName " & pProgram & " \"/home/jrs/SB/mtsb/E01.bas\"")
DLL("scriba_LoadSourceProgram " & pProgram)
DLL("scriba_Run " & pProgram & " \"JRS\"")
DLL("scriba_destroy " & pProgram)
 
cmd = COMMAND()

PRINT "ARG = ",cmd,"\n"

FOR x = 1 TO 10
  PRINT x,"\n"
NEXT
 
root@Laptop:~# ARG = JRS
1
2
3
4
5
6
7
8
9
10

root@Laptop:~#

The only strange thing is that the output of the embedded scriba went to the root console I started sbhttpd in and didn't get returned in the cURL response as I would have expected. It's still cool to see ScriptBasic create an instance of itself in a thread of itself. I think this proves the embedding ScriptBasic in your application is painless and non-intrusive.

Update

Actually the program did exactly what it was suppose to do. When you embed ScriptBasic (in sbhttpd in this case) it uses the stdin/out of the host as a default unless you redefine it with a SB API call.


Support

  • Administrator
  • *****
  • Posts: 1
    • View Profile
Re: MTSB
« Reply #5 on: January 03, 2011, 05:42:56 am »
Here is a Gtk example instantiated multiple times as threads.

INCLUDE curl.bas

ch = curl::init()

curl::option(ch,"NOBODY")
curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/mtgtk.sb")
curl::perform(ch)

curl::option(ch,"NOBODY")
curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/mtgtk.sb")
curl::perform(ch)

curl::finish(ch)
 
PRINT "Status: 200\nContent-Type: text/html\n\n"

DECLARE SUB DLL ALIAS "_idll" LIB "gtk-server"
DECLARE SUB DEFINE ALIAS "_idll_define" LIB "gtk-server"

DEFINE "gtk_init NONE NONE 2 NULL NULL"
DEFINE "gtk_window_new delete-event WIDGET 1 INT"
DEFINE "gtk_window_set_title NONE NONE 2 WIDGET STRING"
DEFINE "gtk_table_new NONE WIDGET 3 INT INT BOOL"
DEFINE "gtk_container_add NONE NONE 2 WIDGET WIDGET"
DEFINE "gtk_button_new_with_label clicked WIDGET 1 STRING"
DEFINE "gtk_table_attach_defaults NONE NONE 6 WIDGET WIDGET INT INT INT INT"
DEFINE "gtk_widget_show_all NONE NONE 1 WIDGET"
DEFINE "gtk_server_callback NONE STRING 1 STRING"
DEFINE "gtk_widget_destroy NONE NONE 1 WIDGET"
DEFINE "g_thread_init NONE NONE 1 NULL"
DEFINE "gdk_threads_init NONE NONE 0"
DEFINE "gdk_threads_enter NONE NONE 0"
DEFINE "gdk_threads_leave NONE NONE 0"

DLL("g_thread_init NULL")
DLL("gdk_threads_init")
DLL("gdk_threads_enter")
DLL("gtk_init NULL NULL")
win = DLL("gtk_window_new 0")
DLL("gtk_window_set_title " & win & " \"SB Gtk\"")
tbl = DLL("gtk_table_new 10 10 1")
DLL("gtk_container_add " & win & " " & tbl)
but = DLL("gtk_button_new_with_label \"Quit\"")
DLL("gtk_table_attach_defaults " & tbl & " " & but & " 5 9 5 9")
DLL("gtk_widget_show_all " & win)

REPEAT
  event = DLL("gtk_server_callback WAIT")
UNTIL event = win OR event = but

DLL("gtk_widget_destroy " & win)
DLL("gtk_server_callback 1")
DLL("gdk_threads_leave")

END
 
Both Gtk windows are independent of each other but can share common variables (read/write lockable) provided by the MT module.

Note: The threading support will be incorporated into a new forked version of GTK-Server that is ScriptBasic extension module specific and static linked in the upcoming mtsb.so extension module. (API scripting and SB embedding in a common module)
« Last Edit: January 03, 2011, 05:49:37 am by support »

Support

  • Administrator
  • *****
  • Posts: 1
    • View Profile
Re: MTSB
« Reply #6 on: January 03, 2011, 05:45:37 am »
Here is an example of embedding a web browser in a ScriptBasic application.

Code: [Select]
DECLARE SUB DLL ALIAS "_idll" LIB "iDLL-Server"
DECLARE SUB REQUIRE ALIAS "_idll_require" LIB "iDLL-Server"
DECLARE SUB DEFINE ALIAS "_idll_define" LIB "iDLL-Server"

REQUIRE "libgtkembedmoz.so"

DEFINE "gtk_init NONE NONE 2 NULL NULL"
DEFINE "gtk_window_new delete-event WIDGET 1 INT"
DEFINE "gtk_window_set_title NONE NONE 2 WIDGET STRING"
DEFINE "gtk_window_set_position NONE NONE 2 WIDGET INT"
DEFINE "gtk_widget_set_size_request NONE NONE 3 WIDGET INT INT"
DEFINE "gtk_window_set_icon_name NONE NONE 2 WIDGET STRING"
DEFINE "gtk_widget_show_all NONE NONE 1 WIDGET"
DEFINE "gtk_moz_embed_set_comp_path NONE NONE 1 STRING"
DEFINE "gtk_moz_embed_set_profile_path NONE NONE 2 STRING STRING"
DEFINE "gtk_moz_embed_new NONE WIDGET 0"
DEFINE "gtk_moz_embed_load_url NONE NONE 2 WIDGET STRING"
DEFINE "gtk_moz_embed_open_stream NONE NONE 3 WIDGET STRING STRING"
DEFINE "gtk_moz_embed_append_data NONE NONE 3 WIDGET STRING INT"
DEFINE "gtk_server_callback NONE STRING 1 STRING"
DEFINE "gtk_server_exit NONE NONE 0"

DLL("gtk_init NULL NULL")
window = DLL("gtk_window_new 0")
DLL("gtk_window_set_title " & window & " \"ScriptBasic Open Source Project\"")
DLL("gtk_window_set_position " & window & " 1")
DLL("gtk_widget_set_size_request " & window & " 700 500")
DLL("gtk_window_set_icon_name " & window & " mozilla")
DLL("gtk_moz_embed_set_comp_path \"/usr/lib/kompozer/\"")
DLL("gtk_moz_embed_set_profile_path \"/tmp\" \"mozilla\"")
moz = DLL("gtk_moz_embed_new")
DLL("gtk_container_add " & window & " " & moz)
DLL("gtk_moz_embed_load_url " & moz & " \"http://www.scriptbasic.org/forum\"")
DLL("gtk_widget_show_all " & window)

REPEAT
  event = DLL("gtk_server_callback WAIT")
UNTIL event = window

DLL("gtk_server_exit")

Quote
GtkMozEmbed is an easy-to-use widget that will allow you to embed a Mozilla browser window into your Gtk application.

patforkin

  • Guest
Re: MTSB
« Reply #7 on: August 12, 2011, 07:27:40 am »
Hello!.
When I look at the MTSB posts I am overwhelmed by an initial complexity.
I do however get the feeling, as I read more, that there is much in it that would help me with the text processing in my translation framework.
How can I begin to explore this potential?
Where should I begin?
Regards, patforkin.

Support

  • Administrator
  • *****
  • Posts: 1
    • View Profile
Re: MTSB
« Reply #8 on: August 12, 2011, 08:11:21 am »
Pat.

MTSB was an effort to use the sbhttpd web server as a multi-threaded application server. Scriba (using cURL) would send request to the server? I was trying to find an easy way to run Gtk in a multi threaded SB desktop environment.

John