Support > General Discussions

MTSB

(1/2) > >>

Support:
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:
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: ---' 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>
"""

--- End code ---

MySQL - customer table

--- Code: ---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 ;

--- End code ---

Support:
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: ---' 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)

--- End code ---

gtkdbweb.sb

--- Code: ---' 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

--- End code ---

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

--- End code ---


faith.cgi

--- Code: ---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

--- End code ---

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)

Support:
Here is an example of ScriptBasic embeding itself in a thread of itself.


--- Code: Text ---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) 

--- Code: Text ---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) 

--- Code: Text ---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.

Navigation

[0] Message Index

[#] Next page

Go to full version