!
! GAMBLE
! Gopher Alpha Micro Browsing/Linking Environment
! Released under the Floodgap Free Software License
!
! Requires XCALLs RENAME, ECHO, NOECHO and ACCEPT.
!
! (C)2020 Cameron Kaiser. All rights reserved.
! http://ampm.floodgap.com/www/gamble.htm * gopher://gopher.floodgap.com/
! [email protected]
!
! GOPHER.LIT is a hacked version of Alpha Micro FINGER.LIT and is released
! under the same terms and conditions as the corresponding version of AlphaTCP,
! including any required and/or applicable SSD restrictions.
!
! Version history
! 1.0     Initial release
!
program gamble,1.0(1)
map1 ctrl'c,f,,1
on error goto g'quit

!!!!
! variable storage
!!!!

! tested on telnet, am-65 and am-75
maxrows=21

! intermediate storage filenames
! these are used for GAMBLE's state
strsiz 10
ibase$="GAMTMP"
iscr$=ibase$ + ".CMD"
istat$=ibase$ + ".TMP"
! this is used for files loaded by GOPHER.LIT. should be ONE CHARACTER ONLY!
! (.lst is appended)
istor$="G"
! this is used for the two intermediates. .LST is appended
istora$="GG"
istorb$="GGG"

! no history ... and doomed to repeat it
history=0

! itypes are single character
strsiz 1
q$=chr(34)
t$=chr(9)
i$="1":dim ih$(10)

! hosts, selectors, arguments top out at 255 characters
strsiz 255
h$="gopher.floodgap.com":dim hh$(10)
s$="/archive/alpha-micro/gopher":dim sh$(10)
ar$="":dim arh$(10)
! we don't need to remember itypes because only menus link to things

! we don't store the port anywhere because GOPHER.LIT, since it uses
! TCP:SERVIC., is limited to the port in that file (i.e., port 70).

! menu items
dim mh$(26)
dim ms$(26)
dim mi$(26)

e$="                                                                             "
a$=""

! used for calculating how long a selector+host is too long
chaff$="gophersqqasg"+istor$
maxlen=92

!!!!
! main program
!!!!

! see if a file to process is available
lookup istorb$ + ".LST",pf:if pf<>0 then goto g'process
! no file available, fall through

! create intermediate script and fetch a selector to a file
g'fetch:
       if instr(1,s$,"@") > 0 or instr(1,h$,"@") > 0 then &
               print "?Cannot use @ in selector or host": &
               end ! XXX
       if instr(1,s$,q$) > 0 or instr(1,h$,q$) > 0 then &
               print "?Cannot use quotes in selector or host": &
               end ! XXX

       ! don't proceed if there is a file clash
       lookup iscr$,x
       if x<>0 then print "?";iscr$;" already exists, aborting":end
       lookup istat$,x
       if x<>0 then print "?";istat$;" already exists, aborting":end

       print tab(-1,32);"Connecting to ";h$;tab(-1,33);
       ! create intermediate script to call SYS:GOPHER.LIT
       open #1,iscr$,output
       print #1 ":R" ! using :S doesn't seem to work right for this
       print #1 "SET REDIR"
       com$ = "GOPHER " + q$ + s$
       if len(ar$) > 0 then com$ = com$ + t$ + ar$
       com$ = com$ + "@" + h$ + q$ + " >" + istor$
       if len(com$) > maxlen then &
               print "?Cannot access a selector this long": &
               end
       print #1 com$
       print #1 "STRCR ";istora$;".LST=";istor$;".LST"
       print #1 "TRIMC ";istorb$;".LST=";istora$;".LST"
       print #1 "RUN TCP:GAMBLE.RUN"
       close #1

       ! store state
       ! we cannot use common to recover state, because common.sbr whacks tcp,
       ! and we probably require too much shared memory; we cannot use cmdlin
       ! because we want to be compatible with regular AlphaBASIC and there's
       ! insufficient space. so, we save our state in a save file.
       open #1,istat$,output
       print #1 history
       print #1 h$
       print #1 s$
       print #1 i$
       print #1 ar$
       for i=1 to 10
               print #1 hh$(i)
               print #1 sh$(i)
               print #1 ih$(i)
               print #1 arh$(i)
       next
       close #1

       ! execute
       chain iscr$
       end

! load and display the menu, as appropriate
! this may be reentrant, so check the status of any intermediate files
g'process:
       ! clean up intermediate script
       lookup iscr$,x: if x<>0 then kill iscr$

       ! retrieve state from save file
       lookup istat$,x
       if x<>0 then &
               open #1,istat$,input: &
               input line #1,a$:history=val(a$): &
               input line #1,h$: &
               input line #1,s$: &
               input line #1,i$: &
               input line #1,ar$: &
               for i=1 to 10: &
                       input line #1,hh$(i): &
                       input line #1,sh$(i): &
                       input line #1,ih$(i): &
                       input line #1,arh$(i): &
               next: &
               close #1: &
               kill istat$

       ! clean up other intermediate files
       ! do this here instead of the script to suppress messages
       lookup istor$+".LST",x:if x<>0 then kill istor$+".LST"
       lookup istora$+".LST",x:if x<>0 then kill istora$+".LST"

       if pf<1 then goto g'itype'empty

       ! deal with dorky terminal drivers
       print tab(-1,9)
       print tab(-1,3);e$
       print tab(-1,3);

       ! treat html like plain text
       if i$="0" or i$="h" then goto g'itype'0
       if i$="1" or i$="7" then goto g'itype'1

       ! fall through to:
g'itype'huh:

       ! save file to disk
       print "Enter filename to save (blank aborts)> ";
       input line a$
       if a$="" then goto g'back

       xcall rename,istorb$+".LST",a$,status
       if status=0 then print "Successfully saved as ";a$:goto g'back

       print "Failed to save to provided filename"
       goto g'itype'huh

g'itype'empty:

       ! file was empty, must have been a problem
       print
       prompt$="Error receiving answer, </> goto, <TAB> back, <ESC> quit"
       gosub g'interface
       if ck = -1 then goto g'back
       if ck = -2 then goto g'navigate
       goto g'itype'empty

g'itype'1:

       ! display and parse menu
       open #1, istorb$ + ".LST", input:rows=0:mitems=0:ditems=0:openf=1
       prompt$="Gopher menu, press letter, </> goto, <TAB> back, <ESC> quit, <SPACE> more"
       key$=""
       print ""
       print ""

       g'itype'10:
               if eof(1) then &
                       prompt$="End of menu, press letter, </> goto, <TAB> back, <ESC> quit, <SPACE> again": &
                       close #1: openf = 0: &
                       for i = rows to maxrows: &
                               print: &
                       next i: &
                       orows = rows:rows = maxrows + 1: &
                       if orows=0 then prompt$="NO DATA, </> goto, <TAB> back, <ESC> quit"
               if rows > maxrows then goto g'itype'12

               ! attempt to parse the line
               input line #1, a$:l=len(a$)
               if l<4 then goto g'itype'10 ! can't possibly be valid

               ! display string/selector. display string can be blank.
               t1=instr(1,a$,t$)
               if t1=0 or t1=l then goto g'itype'10 ! bogus, not RFC1436
               if t1=1 then goto g'itype'10 ! bogus, no item type

               ! selector/host. selector can be blank, host can't.
               t2=instr(t1+1,a$,t$)
               if t2=0 or t2=l then goto g'itype'10 ! still bogus

               ! host/port. neither can be blank.
               t3=instr(t2+1,a$,t$)
               if t3=0 or t3=l then goto g'itype'10 ! still bogus
               if t3=(t2+1) then goto g'itype'10 ! must not be blank

               ! possible to have trailing fields after the port.
               ! these are acceptable, but we ignore them.
               t4=instr(t3+1,a$,t$)

               ! extract item type and display string
               bi$=a$[1;1]:ds$=a$[2,t1-1]:ds$=left(ds$,75)
               rows=rows+1
               ibyte$=">"

               ! display i item type in low intensity
               if bi$="i" then &
                       print " > ";tab(-1,11);ds$;tab(-1,12): &
                       goto g'itype'10
               ! display 3 item type in high intensity
               if bi$="3" then print " ! ";ds$:goto g'itype'10

               ! display other item types in regular intensity underlined
               ! and assign valid menu options a letter key EXCEPT THESE:

               ! * if port != 70, then we can't access it with GOPHER.LIT
               p$=a$[t3+1,-1]:if t4>0 then p$=a$[t3;t4-1]
               if p$<>"70" then print"-";:goto g'itype'11

               ! * if there is a @ or " in the selector or host, we can't
               !   access it with GOPHER.LIT either
               bs$="":if (t2-t1 > 1) then bs$=a$[t1+1,t2-1]
               bh$=a$[t2+1,t3-1]
               if instr(1,bs$,"@") > 0 or instr(1,bh$,"@") > 0 or &
                       instr(1,bs$,q$) > 0 or instr(1,bh$,q$) > 0 then &
                               print"-";:goto g'itype'11

               ! * if the resulting host and selector pair would be too long,
               !   it won't fit in the driver script (wait for the TAMED
               !   version, kids)
               if len(bs$+bh$+chaff$)>maxlen then print "-";:goto g'itype'11

               ! * if this is a hURL, we don't support that
               if left(bs$,4)="URL:" or left(bs$,5)="/URL:" then &
                       print "-";:goto g'itype'11

               ! * if this is itype 2 CSO, we don't support that either
               !   (or Telnet, or TN3270). we could support telnet with a
               !   callout, but that can be done later.
               if bi$="2" or bi$="8" or bi$="T" then print"-";:goto g'itype'11

               ! looks good, let's give it a key
               if bi$<>"0" and bi$<>"1" and bi$<>"h" then ibyte$="$"
               if bi$="7" then ibyte$="?"
               ditems=ditems+1
               mi$(ditems)=bi$
               ms$(ditems)=bs$
               mh$(ditems)=bh$
               print chr(96+ditems);
               if ditems=26 then ditems=0
               mitems=mitems+1:if mitems=27 then mitems=26

       g'itype'11:
               print ibyte$;tab(-1,30);ds$;tab(-1,31)
               goto g'itype'10

       g'itype'12:
               gosub g'interface
               rows = 0
               if ck<>0 and openf<>0 then close #1:openf=0
               if ck = -1 then goto g'back
               if ck = -2 then goto g'navigate
               ! shift-letter: always download (but not itype 7)
               if ck > 64 then &
                       if mi$(ck-64)="7" then &
                               print "Can't download that type of menu item": &
                               goto g'itype'12
               if ck > 64 then &
                       ck = ck - 64: &
                       ni$="9": &
                       nh$=mh$(ck): &
                       ns$=ms$(ck): &
                       goto g'navigate
               ! regular letter: link or download as appropriate
               if ck > 0 then &
                       ni$=mi$(ck): &
                       nh$=mh$(ck): &
                       ns$=ms$(ck): &
                       goto g'navigate
               if eof(1) then goto g'itype'1
               goto g'itype'10

g'itype'0:

       ! display and page the file
       open #1, istorb$ + ".LST", input:rows=0:mitems=0:openf=1
       prompt$="Text file, </> goto, <TAB> back, <ESC> quit, <SPACE> more"

       g'itype'00:
               if eof(1) then &
                       prompt$="End of file, </> goto, <TAB> back, <ESC> quit, <SPACE> again": &
                       close #1: openf=0: &
                       for i = rows to maxrows: &
                               print: &
                       next i: &
                       orows = rows:rows = maxrows + 1: &
                       if orows=0 then prompt$="NO DATA, </> goto, <TAB> back, <ESC> quit"
               if rows > maxrows then goto g'itype'01
               input line #1, a$
               print a$
               rows = rows + 1 + int(len(a$)/79)
               goto g'itype'00

       g'itype'01:
               gosub g'interface
               rows = 0

               if ck<>0 and openf<>0 then close #1:openf=0
               if ck = -1 then goto g'back
               if ck = -2 then goto g'navigate
               if eof(1) then goto g'itype'0
               goto g'itype'00

g'navigate:
       nar$="":if ni$="7" then goto g'itype'7'search
g'navigate'1:
       print "": print ""

       ! assign to history (only if current itype is a menu)
       if i$<>"7" and i$<>"1" then goto g'navigate'2
       history=history+1
       if history=11 then for i=1 to 9: &
               hh$(i)=hh$(i+1):sh$(i)=sh$(i+1):arh$(i)=arh$(i+1): &
               next:history=10
       hh$(history)=h$
       sh$(history)=s$
       arh$(history)=ar$

g'navigate'2:
       i$=ni$
       s$=ns$
       h$=nh$
       ar$=nar$
       gosub g'kill'istorb
       goto g'fetch

g'itype'7'search:
       nar$=""
       print "":print "Enter parameters (blank aborts)> ";
       input line a$
       ! this can only be triggered off a menu option, not the / key
       if a$="" then goto g'itype'1
       if instr(1,a$,q$)<>0 or instr(1,a$,"@")<>0 then &
               print "?Can't use quotes or @": &
               goto g'itype'7'search
       if len(nh$+ns$+t$+a$+chaff$)>maxlen then &
               print "?Too long to query with GAMBLE": &
               goto g'itype'7'search
       nar$=a$:goto g'navigate'1

g'back:
       i$="1"
       h$=hh$(history)
       s$=sh$(history)
       ar$=arh$(history)

       history=history-1
       gosub g'kill'istorb
       goto g'fetch

g'interface:
       print tab(-1,32);prompt$;tab(-1,33);
       xcall noecho:xcall accept,ck:xcall echo
       ! deal with dorky terminal drivers
       print tab(-1,9)
       print tab(-1,3);e$
       print tab(-1,3);
       if ck=32 or ck=10 or ck=13 then ck=0:return
       if ck=9 then &
               if history>0 then &
                       ck=-1:return: &
               else &
                       print "": print "Can't go back any further": &
                       goto g'interface
       if ck=61 then &
               print "": &
               print "Selector: ";s$: &
               print "Host:     ";h$: &
               print "Port:     70":  &
               print "":goto g'interface
       if ck=47 then &
               print "": &
               print "New host> ";:input line nh$: &
               if nh$<>"" then print "New menu selector> ";:input line ns$: &
               ck=-2:ni$="1":return
       if ck=27 then goto g'quit
       if ck>96 then ck=ck-96:if ck<=mitems then return
       if ck>64 then if (ck-64)<=mitems then return
       goto g'interface

g'kill'istorb:
       lookup istorb$+".LST",x:if x<>0 then kill istorb$ + ".LST"
       return

g'quit:
       gosub g'kill'istorb
       print
       print
       print "Goodbye from GAMBLE 1.0 (C)2020 Cameron Kaiser"

       end