home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / k95 / host.ksc < prev    next >
Text File  |  2020-01-01  |  33KB  |  1,182 lines

  1. ; File HOST.KSC - "Host mode" script for K-95.
  2. ;
  3. ; Assumes client is ANSI or VT100 terminal with 24 lines.
  4. ; Protocol operations use APC for automatic up/download,
  5. ; but don't require it.
  6. ;
  7. ;   Works on serial and TCP/IP Telnet connections.
  8. ;   Assumes the connection is already made.  Designed to be
  9. ;   started from HOSTMODE.KSC, which waits for the desired
  10. ;   type of connection to come in.
  11. ;
  12. ; Kermit 95 1.1.17 or later is required.
  13. ;
  14. ; Copyright (C) 1996, 1997, 1998, 1999, 2001
  15. ; Trustees of Columbia University in the City of New York.
  16. ; All rights reserved.   Authors:  F. da Cruz, C. Gianone, J. Altman.
  17. ;
  18. ; Version 1.00: February 1996 for 1.1.3.
  19. ; Version 1.01: 8 June 1997 for 1.1.12:
  20. ;  . Add "set transmit echo off".
  21. ;  . Change "clear device-and-input" to "clear input" in GETMENUITEM.
  22. ; Version 1.02:
  23. ;  . Fix problem with requests to send file groups
  24. ; Version 1.03: 9 July 1998 for 1.1.18
  25. ;  . Fix conflict with FAIL command added in 1.1.17
  26. ;  . Fix potential error if _mypriv is non-numeric
  27. ;  . Make sure Autodownload is OFF
  28. ;  . Add SET TELNET REMOTE-ECHO commands
  29. ; Version 1.04 2 September 1999 for 1.1.18
  30. ;  . Disable TELOPT commands
  31. ;  . Add support for Telnet Authentication
  32. ; Version 1.05 6 April 2000 for 1.1.20
  33. ;  . Change default locations to reference \v(exedir); not \v(startup)
  34. ; Version 1.06 11 Oct 2001 for 1.1.20
  35. ;  . Fixed FAIL to STOP rathr than EXIT and changed its name to BAIL.
  36. ;  . Modernized command continuation and macro definition syntax.
  37.  
  38. def _VERSION 1.06 ; Version of this script
  39.  
  40. ; MACRO DEFINITIONS
  41. ;
  42. ; HOSTLOG writes actions to the screen and to the transaction log.
  43. ; See subsequent redefinitions below.
  44. ;
  45. def HOSTLOG echo \v(time) - \fcontents(\%1)
  46.  
  47. ; BOXMSG prints an attention-getting message on the console screen.
  48. ;
  49. def BOXMSG {
  50.    asg \%9 \frepeat(=,\flen(\%1))
  51.    ec \%9, ec \%1, ec \%9
  52.    beep
  53. }
  54.  
  55. ; LOCK and UNLOCK are for use when updating the user database file,
  56. ; to prevent people from writing over each other's changes.
  57. ;
  58. def UNLOCK {
  59.   if not def _locked end 0
  60.   if exist \m(_lockfile) delete \m(_lockfile)
  61.   undef _locked
  62.   hostlog {Userfile UNLOCKED}
  63.   end 0
  64. }
  65.  
  66. def LOCK {
  67.   if def _locked end 1
  68.   if exist \m(_lockfile) end 1
  69.   open write \m(_lockfile)
  70.   if failure end 1
  71.   writeln file \m(_username)
  72.   if failure end 1
  73.   close write
  74.   if failure end 1
  75.   def _locked 1
  76.   hostlog {Userfile LOCKED}
  77.   end 0
  78. }
  79.  
  80. ; SPLIT and GETFIELDS are for parsing user database records.
  81. ;
  82. def SPLIT {
  83.   asg \%9 \findex(_,\%1)
  84.   asg _LEFT \fbreak(\%1,_)
  85.   asg _RIGHT \fsubstr(\%1,\%9+1)
  86. }
  87.  
  88. def GETFIELDS {
  89.   split {\%1}
  90.   asg U_ID \m(_LEFT)
  91.   split {\m(_RIGHT)}
  92.   asg U_PW \m(_LEFT)
  93.   split {\m(_RIGHT)}
  94.   asg U_PR \m(_LEFT)
  95.   split {\m(_RIGHT)}
  96.   asg U_NM \m(_LEFT)
  97.   split {\m(_RIGHT)}
  98.   asg U_AD \m(_LEFT)
  99.   split {\m(_RIGHT)}
  100.   asg U_TP \m(_LEFT)
  101.   split {\m(_RIGHT)}
  102.   asg U_EM \m(_LEFT)
  103. }
  104.  
  105. ; Make a variable: name is first arg, value is second.
  106. ;
  107. def MAKEVAR2 if def \%2 _assign \%1 \%2, else _assign \%1
  108.  
  109. ; Make a variable from single argument NAME=VALUE
  110. ; Creates variable called "_NAME" with definition "VALUE"
  111. ;
  112. def MAKEVAR {
  113.   if = \findex(=,\%1,1) 0 end
  114.   asg \%9 _\freplace(\%1,=,\32)
  115.   makevar2 \%9
  116. }
  117.  
  118. ; BAIL handles fatal errors
  119. ;
  120. define BAIL {
  121.   hostlog {In \v(cmdfile) at line \v(_line)...}
  122.   hostlog {Fatal error \v(errno) \v(errstring) - session closed}
  123.   beep
  124.   goto badend
  125. }
  126.  
  127. ; SAVEUSERDB saves the user database
  128. ;
  129. define SAVEUSERDB {
  130.   if exist \fdef(_userbak) del \fdef(_userbak)
  131.   rename \fdef(_userfile) \fdef(_userbak)
  132.   if failure hostlog {Warning - Failure to back up user database}
  133.   open write \fdef(_userfile)
  134.   xif failure {
  135.         hostlog {Can not open \fdef(_userfile)}
  136.         UNLOCK
  137.         end 1 
  138.   }
  139.   for \%i 1 \&u[0] 1 {
  140.     writeln file \&u[\%i]
  141.     xif failure {
  142.       hostlog {Error writing record \%i to \fdef(_userfile)}
  143.       hostlog {Old version preserved as \fdef(_userbak)}
  144.       break
  145.     }
  146.   }
  147.   close write
  148.   asg \%9 \v(status)
  149.   if not = \%9 0 hostlog {WARNING - Failed to close \fdef(_userfile)}
  150.   else hostlog {\fdef(_userfile) saved: \&u[0] records}
  151.   UNLOCK
  152.   end \%9
  153. }
  154.  
  155. ; The following code was added to accomodate the new Windows multi-user
  156. ; friendly directory layouts introduced with version 1.1.21.  We have
  157. ; check several locations for the directories that hostmode uses and
  158. ; create them in \v(appdata) if the do not exist at all.
  159.  
  160. if directory \v(exedir)USERS {
  161.     asg _hostroot \v(exedir)
  162.     forward FOUNDDIRS
  163. }
  164. if directory \v(startup)INCOMING {
  165.     asg _hostroot \v(startup)
  166.     forward FOUNDDIRS
  167. }
  168.  
  169. if directory \v(appdata)USERS {
  170.     asg _hostroot \v(appdata)
  171. }
  172.  
  173. :FOUNDDIRS
  174.  
  175. ; CONFIGURATION: Defaults in case the config file (HOST.CFG) gets lost...
  176. ;
  177. asg _maxusers 100            ; Maximum number of users in database
  178. asg _inactivity 1800            ; Logged-in inactivity limit (seconds)
  179. asg _logintime 300            ; Inactivity limit while logging in
  180. asg _anonok 1                ; Anonymous logins OK (0 = not OK)
  181. asg _logging 1                ; Logging enabled (0 = skip logging)
  182. asg _dlincoming 0            ; OK to download from INCOMING dir
  183. asg _msgmax 200                ; Longest message size (lines)
  184. asg _protocol kermit            ; Default file transfer protocol
  185. asg _xfermode binary            ; Default file transfer mode
  186. asg _owner THE PROPRIETOR        ; PC owner's name or company
  187. asg _herald Welcome to K-95 Host Mode    ; Main screen title
  188. asg _public   \m(_hostroot)PUBLIC    ; Directory users can get files from
  189. asg _incoming \m(_hostroot)INCOMING    ; Directory that users can send file to
  190. asg _logdir   \m(_hostroot)LOGS        ; Directory for host-mode logs
  191. asg _usertree \m(_hostroot)USERS    ; Root of user directory tree
  192. asg _tmpdir   \v(tmpdir)        ; Directory for temp files
  193. if not def _tmpdir asg _tmpdir \m(_hostroot)TMP
  194. asg _userfile \m(_usertree)/USERS.DAT     ; User database file
  195. asg _greeting \m(_usertree)/GREETING.TXT ; Message/greeting text filename
  196. asg _helpfile \m(_usertree)/HOSTMODE.TXT ; Host-mode help file
  197. asg _msgfile  \m(_usertree)/MESSAGES.TXT ; Messages for proprietor
  198.  
  199. ; Now read the configuration file.
  200. ; Note that the name and subdirectory are hardwired.
  201. ;
  202. asg _configfile \freplace(\m(_hostroot)host.cfg,/,\\)
  203. asg _mypriv 0
  204.  
  205. if not exist \m(_configfile) forward noconfig
  206. open read \m(_configfile)
  207. if failure forward noconfig
  208. while true { read \%a, if failure break, makevar \%a }
  209.  
  210. :NOCONFIG
  211.  
  212. ; END OF CONFIGURATION SECTION
  213.  
  214. dcl \&u[\m(_maxusers)]
  215.  
  216. if not def _lockfile asg _lockfile \m(_usertree)/USERS.LCK
  217.  
  218. if not exist \m(_userfile) -
  219.   if not eq "\m(_anonok)" "1" -
  220.     stop 1 Fatal - User database not found and guest logins are disabled.
  221.  
  222. asg _userbak \freplace(\m(_userfile),.DAT,.BAK)    ; Name of backup file
  223.  
  224. ; CD to where the user directories are.
  225. ;
  226. cd \m(_usertree)
  227. if failure stop 1 Fatal - Can't change directory to "\m(_usertree)"
  228.  
  229. ; And then CD to its parent.
  230. ;
  231. cd ..
  232. if failure stop 1 Fatal - Can't change directory to "\m(_usertree)/.."
  233.  
  234. asg _startdir \v(dir)            ; Host-mode "home" directory.
  235.  
  236. ; Create needed directories if they don't exist.
  237. ;
  238. if not directory \m(_incoming) mkdir \m(_incoming)
  239. if not directory \m(_incoming) stop 1 Fatal - no INCOMING directory
  240.  
  241. if not directory \m(_public) mkdir \m(_public)
  242. if not directory \m(_public) stop 1 Fatal - no PUBLIC directory
  243.  
  244. if not directory \m(_usertree) mkdir \m(_usertree)
  245. if not directory \m(_usertree) stop 1 Fatal - no USERS directory
  246.  
  247. if not directory \m(_tmpdir) mkdir \m(_tmpdir)
  248. if not directory \m(_tmpdir) stop 1 Fatal - no TMP directory
  249.  
  250. if eq "\m(_logging)" "1" -
  251.   if not dir \m(_logdir) mkdir \m(_logdir) ; Not fatal if this fails
  252.  
  253. if exist \m(_msgfile) boxmsg {You have messages in \m(_msgfile)!}
  254.  
  255. ; SETTINGS...
  256. ;
  257. set input echo off            ; Keep host PC screen clean
  258. set exit warning off            ; ...
  259. set file display quiet            ; ...
  260. set case off                ; Ignore case in string comparisons 
  261. set delay 1                ; Delay in starting file transfers
  262. set file type binary            ; Transfer mode is binary by default
  263. set transmit prompt 0            ; No line turnaround on TRANSMIT
  264. set transmit linefeed on        ; Keep linefeeds when transmitting
  265. set transmit echo off                   ; No echo during TRANSMIT
  266. set file char cp437            ; For PC-format text files
  267.  
  268. set file names converted        ; No weird stuff in filenames
  269. set receive pathnames off        ; Strip pathnames from incoming files
  270. set send pathnames off            ; and outbound pathnames too
  271. set file collision overwrite        ; Overwrite incoming files by default
  272.  
  273. set input autodownload off
  274. set terminal autodownload off
  275. if >= \v(xversion) 1118 { set telnet remote-echo off }
  276.  
  277. if < \v(xversion) 1118 {
  278.  ; If we are a Telnet server we need to control the echoing ourselves.
  279.  ;
  280.  if not = \findex(tcp,\v(connection),1) 1 forward NOTELOPT
  281.  ;
  282.  ; Ex-post-facto Telnet "negotiations" to undo whatever might have been
  283.  ; negotiated already.  K95 normally is a client, but now it is a server.
  284.  ;
  285.  telopt will echo            ; I must echo
  286.  if failure bail            ; Make sure this does not fail
  287.  telopt dont echo            ; You must not echo
  288.  telopt will sga            ; Suppress Go-Ahead
  289.  telopt wont ttype            ; No terminal type negotiations
  290.  telopt wont naws            ; No screen-size negotiations
  291. }
  292.  
  293. :NOTELOPT
  294. def ERROR msg {\%1}, sleep 2, def _status 1, goto main
  295. def FATAL msg {Fatal - {\%1}}, sleep 2, fail
  296.  
  297. hostlog {\v(date) - Start host script \v(cmdfil)}
  298. hostlog {Current directory: \freplace(\v(dir),\\,/)}
  299.  
  300. ; Macros for screen formatting using VT100/ANSI escape sequences
  301.  
  302. define clr output \27[H\27[2J, if failure bail   ; Clear screen
  303. define cur output \27[\%1;\%2H, if failure bail  ; Position the cursor
  304. define atp cur \%1 \%2, out \%3, if failure bail ; Print text at cursor pos
  305. define cleol out \27[K, if failure bail          ; Clear to end of line
  306.  
  307. ; Returns basename of DOS/Windows-format filename argument \%1;
  308. ; that is, the filename stripped of disk letter and/or directory path, if any.
  309. ;
  310. define BASENAME {
  311.   asg \%9 \frindex(:,\%1)
  312.   asg \%8 \frindex(/,\%1)
  313.   asg \%7 \frindex(\\,\%1)
  314.   asg \%6 \fmax(\%9,\%8)
  315.   asg \%6 \fmax(\%7,\%6)
  316.   return \fsubstr(\%1,\%6+1)
  317. }
  318.  
  319. ; Print message in message area - line \%M column \%C
  320. define MSG cur \%M \%C, cleol, if def \%1 out \fcont(\%1)
  321.  
  322. ; The next two are used with "Leave a message".
  323. ; \%k is the message line number, global, don't use for anything else.
  324. ;
  325. def ADDLINE incr \%k, asg \&a[\%k] \fcontents(\%1)
  326. def NEWSCREEN def \%L 1, clr
  327.  
  328. ; Erases a character on the input line and from the input variable \%n.
  329. ;
  330. define BS {
  331.   if not > \flen(\%n) 0 end 0
  332.   asg \%n \fsubstr(\%n,1,\flen(\%n)-1)
  333.   decr \%p, out \27[D, cleol, end 0
  334. }
  335.  
  336. ; GETMENUITEM reads a number into \%n, using the echo line, \%L.
  337. ; The argument (\%1) is the label to jump to if the menu needs to be repainted.
  338. ; The minimum value is 1, the maximum value is the second argument, \%2.
  339. ; The prompt is "Enter choice: ".
  340. ;
  341. define GETMENUITEM {
  342.   atp \%L \%C {Enter choice: }
  343. :NEW
  344.   asg \%p \feval(\%C+14)
  345.   asg \%q \%p
  346.   def \%n
  347. :INLOOP1
  348.   clear input
  349.   cur \%L \%p
  350.   cleol
  351.   input \m(_inactivity)
  352.   if failure bail
  353.   if eq "" "\v(inchar)" goto inloop1
  354.   asg \%9 \fcode(\v(inchar))
  355.   if = \%9   9 asg \%9 32
  356.   if = \%9 127 asg \%9 8
  357.   if = \%9   8 { bs, goto inloop1 }
  358.   if = \%9  21 { cur \%L \%q, cleol, asg \%p \%q, asg \%n, goto inloop1 }
  359.   if = \%9   3 goto main
  360.   if = \%9  12 goto \%1
  361.   if not < \%9 32 forward graphic
  362.   if not def \%n goto inloop1
  363.   forward gotit
  364. :GRAPHIC
  365.   atp \%L \%p \v(inchar)
  366.   incr \%p
  367.   if eq \%9 32 { if def \%n forward GOTIT, else goto inloop1 }
  368.   if not numeric \v(inchar) { msg {"\v(inchar)" - Not a number}, goto new }
  369.   if not def \%n asg \%n 0
  370.   asg \%n \feval(10 * \%n + \v(inchar))
  371.   msg
  372.   goto inloop1
  373. :GOTIT
  374.   msg {Your choice: \%n}
  375.   if not def \%n goto \%1
  376.   if not numeric \%n goto \%1
  377.   if > \%n 0 if not > \%n \%2 end 0
  378.   msg {\%n - Out of range}
  379.   goto new
  380. }
  381.  
  382. ; INTEXT reads a line of text from user on the input line, \%L, allows editing
  383. ; with Backspace or Del (erase characters), Ctrl-U (erase line), etc.
  384. ; Can be interrupted with Ctrl-C if _ctrlc is defined (it should be defined
  385. ; as a help string to be printed).
  386. ;
  387. ; Terminates on space or any control character except BS, Del, or Ctrl-U.
  388. ; The techniques used for reading and echoing characters are designed to work
  389. ; on both serial and Telnet connections, even whe Telnet echoing is
  390. ; misnegotiated.
  391. ;
  392. ; Argument 1 is the input (echo) line number.
  393. ; Argument 2 is the prompt.
  394. ; Argument 3 is 32 to break on space or less, 31 to break only on control 
  395. ;  chars (use 32 to read a word, use 31 to read a line of text).
  396. ; Argument 4, if included, is the timeout value.
  397. ; Argument 5, if included, is a char to echo in place of what was typed.
  398. ;
  399. ; Returns:
  400. ;   0 on success with \%n set to the text that was input.
  401. ;   1 if Ctrl-C was typed
  402. ;
  403. define INTEXT {
  404.   if not def \%3 asg \%3 32
  405.   if not def \%4 asg \%4 \m(_inactivity)
  406.   if def \%5 if > \flen(\%5) 1 asg \%5 \fsubstr(\%5,1,1)
  407.   def \%n
  408.   asg \%L \%1
  409.   asg \%p \feval(\flen(\%2)+\%C)
  410.   asg \%q \%p
  411.   atp \%L \%C {\%2}
  412.   if def _ctrlc atp \feval(\%L+1) \%C {\m(_ctrlc)}
  413. :INLOOP2
  414.   cur \%L \%p
  415.   cleol
  416.   input \%4
  417.   if failure bail
  418.   if eq "\v(inchar)" "" goto inloop2
  419.   asg \%9 \v(inchar)
  420.   asg \%8 \fcode(\v(inchar))
  421.   if = \%8 3 if def _ctrlc end 1
  422.   if = \%8   9 { asg \%8 32, asg \%9 \32 }
  423.   if = \%8   8 { bs, goto inloop2 }
  424.   if = \%8 127 { bs, goto inloop2 }
  425.   if = \%8  21 {
  426.     cur \%L \%q, cleol, asg \%p \%q, asg \%n, goto inloop2
  427.   }
  428.   if > \%8 \%3 { 
  429.       asg \%n \fcontents(\%n)\fcontents(\%9)
  430.       if def \%5 atp \%L \%p \%5
  431.       else atp \%L \%p \fcont(\%9)
  432.       incr \%p
  433.       goto inloop2
  434.   }
  435.   if eq "" "\%n" goto inloop2
  436.   end 0
  437. }
  438.  
  439. ; Displays a text file on the user's screen
  440. ;
  441. def DISPLAY {
  442.   hostlog {Typing \%1}
  443.   asg \%9 \v(ftype)
  444.   set file type text
  445.   output \13\10\10
  446.   transmit \%1
  447.   asg _status \v(status)
  448.   set file type \%9
  449.   out \10\13Use scrollback to view any text that scrolled off the screen.
  450.   out \10\13Press any key to continue...
  451.   input \m(_inactivity)
  452.   end 0
  453. }
  454.  
  455. ;+------------------------------------------------------------------------
  456. ; LOGIN procedure
  457. ;
  458.  
  459. asg \%C 1     ; Left margin column for login procedure.
  460. asg \%M 6     ; Row for messages.
  461. undef _ctrlc  ; Ctrl-C disabled during login process!
  462. undef _locked
  463.  
  464. hostlog {Connection from \v(line)}
  465. clr
  466.  
  467. atp 1 1 {K-95 Login - Initializing...}
  468.  
  469. if < \v(xversion) 1118 { 
  470.   msleep 2000                ; Wait for TELNET option replies
  471.   clear device-buffer            ; and then clear them out
  472. }
  473.  
  474. hostlog {Auth State = [\v(authstate)]}
  475. hostlog {Auth Name  = [\v(authname)]}
  476. hostlog {Auth Type  = [\v(authtype)]}
  477. hostlog {User name  = [\v(user)]}
  478.  
  479. if eq "\v(authstate)" "valid" {
  480.   asg _username \v(user)
  481.   def ok 0
  482.   if not exist \m(_userfile) forward AUTHBAD
  483.   open read \m(_userfile)
  484.   if failure forward AUTHBAD
  485. :AUTHLOOP
  486.   read \%a
  487.   if failure forward AUTHDONE
  488.   getfields {\%a}
  489.   if not eq "\m(U_ID)" "\m(_username)" goto AUTHLOOP
  490.   def ok 1
  491. :AUTHDONE
  492.   close read
  493.   if > \m(ok) 0 forward AUTHGOOD
  494. :AUTHBAD
  495.   hostlog {Access denied "\m(_username)"}
  496.   undef _password
  497.   if count goto again
  498.   msg {Access denied - hanging up}
  499.   incr \%M
  500.   msg
  501.   hostlog {Invalid user - access denied}
  502.   bail
  503. :AUTHGOOD
  504.   undef _password
  505.   asg _myname \fdef(U_NM)
  506.   asg _mypriv \m(U_PR)
  507.   if not numeric \m(_mypriv) asg _mypriv 0
  508.   msg {\m(_username) authenticated by \v(authtype)}
  509.   beep
  510.   if >= \v(xversion) 1118 { set telnet remote-echo on }
  511.   undef noecho
  512.   forward LOGGEDIN
  513. }
  514.  
  515. if eq "\v(authstate)" "user" {
  516.   asg _username \v(user)
  517.   forward GETPASSWD
  518. }
  519.  
  520. set count 3                ; Allow three tries to log in
  521. :AGAIN                    ; Login retry loop
  522. clr
  523. atp 1 1 {K-95 Login}
  524. if < \v(count) 3 { msg {Access denied}, sleep 3 }
  525. def \%L 1
  526. define noecho
  527. if >= \v(xversion) 1118 { set telnet remote-echo on }
  528. clear device-buffer            ; Don't allow typeahead
  529. intext 3 {Username: } 32 90
  530. if failure bail
  531. asg _username \%n
  532. if not eq "\m(_anonok)" "1" forward GETPASSWD
  533. if not eq "\m(_username)" "guest" forward GETPASSWD
  534.  
  535. msg                    ; GUEST user
  536. asg _myname {Anonymous Guest}
  537. asg _mypriv 0
  538. msg
  539. beep
  540. if >= \v(xversion) 1118 { set telnet remote-echo on }
  541. undef noecho
  542. forward LOGGEDIN
  543.  
  544. :GETPASSWD
  545. def noecho on
  546. if >= \v(xversion) 1118 { set telnet remote-echo off }
  547. clear device-buffer
  548. intext 4 {Password: } 32 90 *
  549. if failure bail
  550. asg _password \f.oox(\%n)
  551. asg \%n
  552.  
  553. :CHECKPASSWD
  554.   def ok 0
  555.   if not exist \m(_userfile) forward BAD
  556.   open read \m(_userfile)
  557.   if failure forward BAD
  558. :PWLOOP
  559.   read \%a
  560.   if failure forward PWDONE
  561.   getfields {\%a}
  562.   if not eq "\m(U_ID)" "\m(_username)" goto PWLOOP
  563.   if not eq "\m(U_PW)" "\m(_password)" goto PWLOOP
  564.   def ok 1
  565. :PWDONE
  566.   close read
  567.   if > \m(ok) 0 forward good
  568. :BAD
  569.   hostlog {Access denied "\m(_username)"}
  570.   undef _password
  571.   if count goto again
  572.   msg {Access denied - hanging up}
  573.   incr \%M
  574.   msg
  575.   hostlog {Incorrect password - access denied}
  576.   bail
  577. :GOOD
  578.   undef _password
  579.   asg _myname \fdef(U_NM)
  580.   asg _mypriv \m(U_PR)
  581.   if not numeric \m(_mypriv) asg _mypriv 0
  582.   msg
  583.   beep
  584.   if >= \v(xversion) 1118 { set telnet remote-echo on }
  585.   undef noecho
  586.  
  587. ;+------------------------------------------------------------------------+
  588. ; Get here when logged in.
  589.  
  590. :LOGGEDIN
  591.  
  592. ; Create Transaction log with unique name "<username>_<julian-date>_<time>.log"
  593. ;
  594. if eq "\m(_logging)" "1" {
  595.   log transactions \m(_logdir)/\m(_username)_\v(ndate)_\v(ntime).log
  596.   if failure hostlog {Warning - can't create log file}
  597.   else def HOSTLOG { echo \v(time) - \fcont(\%1)
  598.     writeln trans \v(time) - \fcont(\%1) }
  599. }
  600. hostlog {Login by \m(_username) (\m(_myname))}
  601.  
  602. if exist \m(_greeting) display \m(_greeting)
  603.  
  604. :CHKANON ; Check for anonymous GUEST login
  605.   if not eq "\m(_username)" "guest" forward USERCD
  606.   cd \m(_public)
  607.   asg _current PUBLIC
  608.   undef _userdir
  609.   forward START
  610.  
  611. :USERCD ; Create user's directory if necessary and then CD to it.
  612.  
  613. asg _userdir \m(_usertree)/\m(_username)
  614. if not directory \m(_userdir) {
  615.   hostlog {Creating \m(_userdir)}
  616.   mkdir \m(_userdir)
  617.   if failure fatal {Unable to create your directory}
  618. }
  619. cd \m(_userdir)
  620. if failure fatal {Unable to access your directory}
  621.  
  622. asg _current USER
  623.  
  624. :START
  625.   dcl \&s[2]            ; Status messages
  626.   def \&s[0] OK
  627.   def \&s[1] FAILED
  628.   def \&s[2] UNKNOWN
  629.  
  630.   dcl \&p[5]            ; Protocol names
  631.   def \&p[1] kermit
  632.   def \&p[2] zmodem
  633.   def \&p[3] ymodem
  634.   def \&p[4] ymodem-g
  635.   def \&p[5] xmodem
  636.  
  637.   define \%C 20 ; Left margin column for menu
  638.   hostlog {Enter main menu}
  639.   undef _csave
  640.   undef _inmail
  641.   undef _status
  642.   set protocol \m(_protocol)
  643.   set file type \m(_xfermode)
  644.  
  645.   def _priv_cd  1
  646.   def _priv_dos 2
  647.   def _idle_limit (none)
  648.   if not numeric _inactivity asg _inactivity 1800
  649.   if > \m(_inactivity) 0 asg _idle_limit \m(_inactivity)
  650.  
  651. :MAIN
  652.   if def _locked unlock
  653.   if def _inmail { hostlog {Message canceled}, undef _inmail }
  654.   dcl \&a[0]
  655.   def _ctrlc (Ctrl-C to return to main menu) ; Ctrl-C enabled now
  656.   if def _csave { asg \%C \m(_csave), def _csave } ; Left margin
  657.  
  658. clr
  659. atp  1 \%C {\m(_herald) V\m(_VERSION)}
  660. atp  3 \%C {Current directory: \freplace(\v(dir),\\,/)}
  661. atp  4 \%C {Protocol: \v(protocol), Transfer mode: \fcaps(\v(ftype))}
  662. if exist \m(_usertree)/\m(_username).MSG -
  663. atp  5 \%C {Message(s) waiting...}
  664. else atp  5 \%C {Idle limit: \m(_idle_limit) sec -- Choices:}
  665. atp  7 \%C { 1 - Change protocol}
  666. atp  8 \%C { 2 - Change transfer mode}
  667. atp  9 \%C { 3 - Change directory}
  668. atp 10 \%C { 4 - List files}
  669. atp 11 \%C { 5 - Download files}
  670. atp 12 \%C { 6 - Upload files}
  671. atp 13 \%C { 7 - View a file}
  672. atp 14 \%C { 8 - Delete files}
  673. atp 15 \%C { 9 - Read messages}
  674. atp 16 \%C {10 - Leave a message}
  675. atp 17 \%C {11 - Change password}
  676. atp 18 \%C {12 - Help}
  677. atp 19 \%C {13 - Logout}
  678. if not > \m(_mypriv) 1 forward NODOS
  679. atp 20 \%C {14 - Execute a DOS command}
  680. if def _status -
  681. atp 24 \%C {Last command: \&s[\m(_status)]}
  682. asg \%H 14             ; Highest menu item
  683. asg \%L 22             ; Menu input line
  684. forward getmenu
  685. :NODOS                 ; DOS commands not allowed
  686. if def _status -
  687. atp 23 \%C {Last command: \&s[\m(_status)]}
  688. asg \%H 13
  689. asg \%L 21
  690. :GETMENU
  691. asg \%M \feval(\%L+1)  ; Message line
  692. getmenuitem main \%H   ; Get user's choice
  693. if eq "" "\%n| {
  694.         hostlog {Undefined LBL_ value}
  695.         bail
  696. }
  697. forward LBL_\%n        ; Go forward and handle it
  698.  
  699. ;+------------------------------------------------------------------------+
  700. ; Host mode actions
  701.  
  702. :LBL_1 ; PROTOCOL
  703. clr
  704. atp  4 \%C {SELECT PROTOCOL}
  705. atp  6 \%C {Current protocol: \v(protocol)}
  706. atp  8 \%C {Choices:}
  707. atp 10 \%C {1 - Kermit}
  708. atp 11 \%C {2 - ZMODEM}
  709. atp 12 \%C {3 - YMODEM}
  710. atp 13 \%C {4 - YMODEM-G}
  711. atp 14 \%C {5 - XMODEM}
  712. atp 15 \%C {6 - Return to main menu}
  713.  
  714. asg \%L 17             ; Menu input line
  715. asg \%M \feval(\%L+2)  ; Message line
  716. getmenuitem lbl_1 6
  717. if not = \%n 6 set protocol \&p[\%n]
  718. asg _status \v(status)
  719. goto main
  720.  
  721. :LBL_2 ; TRANSFER MODE
  722. clr
  723. atp  8 \%C {SELECT TRANSFER MODE}
  724. atp 10 \%C {Current mode: \v(ftype)}
  725. atp 12 \%C {Choices:}
  726. atp 14 \%C {1 - Binary}
  727. atp 15 \%C {2 - Text}
  728. atp 16 \%C {3 - Return to main menu}
  729.  
  730. asg \%L 18             ; Menu input line
  731. asg \%M \feval(\%L+2)  ; Message line
  732. getmenuitem lbl_2 3
  733. if = \%n 1 set file type binary
  734. else if = \%n 2 set file type text
  735. asg _status \v(status)
  736. goto main
  737.  
  738. :LBL_5 ; DOWNLOAD
  739. clr
  740. if > \m(_mypriv) 0 forward DLOK
  741. if eq "\m(_dlincoming)" "1" forward DLOK
  742. if not eq "\m(_current)" "INCOMING" forward DLOK
  743. error {Sorry - Read access to INCOMING directory not allowed.}
  744. :DLOK
  745. atp  4 \%C {DOWNLOAD FILES}
  746. atp  6 \%C {Protocol:      \v(protocol)}
  747. atp  7 \%C {Transfer mode: \v(ftype)}
  748. atp  9 \%C {Type a single file specification.}
  749. if eq "\v(protocol)" "XMODEM" forward DLNAME
  750. atp 11 \%C {To select multiple files:}
  751. atp 12 \%C {include * and/or ? in the filename.}
  752. :DLNAME
  753. intext \%L {File(s) to download: }
  754. if failure goto main
  755. if not def \%n goto main
  756. asg \%n \fexec(basename \%n)
  757. if not > \ffiles(\%n) 0 error {\%n - File not found}
  758. hostlog {Sending \%n}
  759. if eq "\v(protocol)" "kermit" apc receive
  760. msg {Please escape back and initiate a \v(protocol) RECEIVE...}
  761. sleep 1
  762. send \%n
  763. asg _status \v(status)
  764. if = \m(_status) 0 hostlog {Send OK, \v(cps) CPS}
  765. else hostlog {Send failed}
  766. goto main
  767.  
  768. :LBL_6 ; UPLOAD
  769. clr
  770. if > \m(_mypriv) 0 forward UPLOADOK
  771. if not eq "\m(_current)" "PUBLIC" forward UPLOADOK
  772. atp 4 \%C {Sorry - no uploading to the PUBLIC directory.}
  773. atp 5 \%C {Please change to the INCOMING directory or to}
  774. atp 6 \%C {your own home directory prior to uploading.}
  775. sleep 2
  776. asg _status 1
  777. goto main
  778. :UPLOADOK
  779. atp 4 \%C {UPLOAD FILES}
  780. atp 6 \%C {Protocol:      \v(protocol)}
  781. atp 7 \%C {Transfer mode: \v(ftype)}
  782. intext \%L {File(s) to upload: }
  783. if failure goto main
  784. if not def \%n goto main
  785. if not eq "\v(protocol)" "kermit" forward XYZMODEM
  786. apc server
  787. atp 12 \%C {Please escape back and enter SERVER mode...}
  788. sleep 1
  789. beep info
  790. hostlog {Receiving \%n in \freplace(\v(dir),\\,/)}
  791. get \%n
  792. asg _status \v(status)
  793. if = \m(_status) 0 hostlog {Get OK, \v(cps) CPS}
  794. else hostlog {Get failed}
  795. sleep 1
  796. finish
  797. goto main
  798. :XYZMODEM
  799. atp 12 \%C {Please return to your client software and instruct}
  800. atp 13 \%C {it to send \%n using \v(protocol) protocol.}
  801. receive
  802. asg _status \v(status)
  803. goto main
  804.  
  805. :LBL_7 ; TYPE
  806. clr
  807. if > \m(_mypriv) 0 forward TYPEOK
  808. if not eq "\m(_current)" "INCOMING" forward TYPEOK
  809. if eq "\m(_dlincoming)" "1" forward TYPEOK
  810. error {Sorry - Read access to the INCOMING directory not allowed.}
  811. :TYPEOK
  812. atp 8 \%C {TYPE A FILE}
  813. intext \%L {File to type: }
  814. if failure goto main
  815. asg \%n \fexec(basename \%n)
  816. if not exist \%n error {\%n - File not found}
  817. display \%n
  818. goto main
  819.  
  820. :LBL_8 ; DELETE
  821. clr
  822.  
  823. if > \m(_mypriv) 0 forward DELOK
  824. if eq "\m(_current)" "USER" forward DELOK
  825. error {Sorry - You may delete files only in your own directory.}
  826. :DELOK
  827. atp 8 \%C {DELETE FILES}
  828. intext \%L {File(s) to delete: }
  829. if failure goto main
  830. asg \%n \fexec(basename \%n)        ; Strip disk and directory
  831. if not > \ffiles(\%n) 0 error {\%n - Not found}
  832. delete \%n
  833. asg _status \v(status)
  834. goto main
  835.  
  836. :LBL_4 ; DIRECTORY
  837. clr
  838. if > \m(_mypriv) 0 forward DIROK
  839. if eq "\m(_dlincoming)" "1" forward DIROK
  840. if not eq "\m(_current)" "INCOMING" forward DIROK
  841. error {Sorry - Read access to the INCOMING directory not allowed.}
  842. :DIROK
  843. atp  8 \%C {VIEW DIRECTORY LISTING}
  844. hostlog {Sending directory listing}
  845. cur 10 1
  846. asg _tmpfile \m(_tmpdir)K_\v(ntime).TMP
  847. run dir > \freplace(\m(_tmpfile),/,\\)
  848. display \m(_tmpfile)
  849. delete \m(_tmpfile)
  850. goto main
  851.  
  852. :LBL_3 ; CD
  853. clr
  854. atp   8 \%C {CHANGE DIRECTORY}
  855. atp  10 \%C {Current directory: \freplace(\v(dir),\\,/)}
  856. atp  12 \%C {Choices:}
  857. atp  14 \%C {1 - My home directory (read/write only by me)}
  858. atp  15 \%C {2 - The PUBLIC directory (everybody can read it)}
  859. if eq "\m(_dlincoming)" "1" -
  860. atp  16 \%C {3 - The INCOMING directory (everybody can read and write)}
  861. else -
  862. atp  16 \%C {3 - The INCOMING directory (everybody can write)}
  863.  
  864. if > \m(_mypriv) 0 -
  865. atp  17 \%C {4 - A specific directory}
  866. atp  18 \%C {5 - Return to main menu}
  867.  
  868. asg \%L 20             ; Menu input line
  869. asg \%M \feval(\%L+2)  ; Message line
  870.  
  871. getmenuitem LBL_3 5    ; Repaint menu at LBL_3, 5 items in menu
  872. forward cd_\%n         ; Dispatch to chosen menu item
  873.  
  874. :CD_1 ; CD to home directory
  875. if not def _userdir goto main        ; Not for anonymous users
  876. cd \m(_userdir)                ; Real user, CD to own directory
  877. asg _status \v(status)            ; Remember status
  878. enable send                ; Uploads are allowed
  879. set file collision ov            ; OK to overwrite files
  880. asg _current USER
  881. goto main
  882.  
  883. :CD_2 ; CD to PUBLIC directory
  884. cd \m(_public)                ; Everybody can do this
  885. asg _status \v(status)
  886. if < \m(_mypriv) 1 disable send        ; Sending to here not allowed
  887. else enable send
  888. set file collision ov            ; Write over files on uploads
  889. asg _current PUBLIC
  890. goto main
  891.  
  892. :CD_3 ; CD to INCOMING directory
  893. cd \m(_incoming)            ; Everone can come here
  894. asg _status \v(status)
  895. set file collision ov            ; Overwrite files on upload
  896. asg _current INCOMING
  897. enable send                ; It's always OK to upload
  898. goto main
  899.  
  900. ; CD to a given directory.
  901. ; Note the use of \fcontents() to prevent overevaluation of backslashes.
  902. ;
  903. :CD_4 ; CD to a specific directory
  904. if not > \m(_mypriv) 0 error {Sorry - insufficient privilege}
  905. intext \%L {Enter directory name: }
  906. if failure goto LBL_3
  907. if not def \%n goto LBL_3
  908. if not dir \fcont(\%n) error {\%n - not a directory}
  909. cd \fcont(\%n)
  910. asg _status \v(status)
  911. if > \m(_status) 0 error {Failure to change directory to "\fcont(\%n)"}
  912. set file collision backup        ; Overwrite would be dangerous here!
  913. asg _current SPECIAL            ; But RENAME is too confusing...
  914. enable send                ; I'm privileged so I can upload.
  915. goto main
  916.  
  917. :CD_5
  918. goto main
  919.  
  920. :LBL_9 ; CHECK / READ MESSAGES
  921. clr
  922. if not exist \m(_usertree)/\m(_username).MSG forward NOMSGS
  923. display \m(_usertree)/\m(_username).MSG
  924.  
  925. :MSGDISP
  926. clr
  927. atp  8 \%C {MESSAGE DISPOSITION}
  928. atp 10 \%C {Choices:}
  929. atp 12 \%C {1 - Redisplay}
  930. atp 13 \%C {2 - Delete}
  931. atp 14 \%C {3 - Save in MESSAGES.TXT}
  932. atp 15 \%C {4 - Download}
  933. atp 16 \%C {5 - Return to main menu}
  934. asg \%L 18             ; Menu input line
  935. asg \%M \feval(\%L+2)  ; Message line
  936. getmenuitem msgdisp 5
  937. forward MSG_\%n
  938. :MSG_1
  939.   goto lbl_9
  940. :MSG_2
  941.   delete \m(_usertree)/\m(_username).MSG
  942.   asg _status \v(status)
  943.   goto main
  944. :MSG_3
  945.   asg \%9 \freplace(\v(dir),\\,/)
  946.   cd \m(_usertree)
  947.   run copy \m(_username)\\MESSAGES.TXT+\m(_username).MSG -
  948.     \m(_username)\\MESSAGES.TXT
  949.   asg _status \v(status)
  950.   cd \%9
  951.   if = \m(_status) 0 delete \m(_usertree)/\m(_username).MSG
  952.   goto main
  953. :MSG_4
  954.   hostlog {Sending \m(_usertree)/\m(_username).MSG}
  955.   if eq "\v(protocol)" "kermit" apc receive
  956.   msg {Please escape back and initiate a \v(protocol) RECEIVE...}
  957.   asg \%9 \v(ftype)
  958.   set file type text
  959.   sleep 1
  960.   send \m(_usertree)/\m(_username).MSG
  961.   asg _status \v(status)
  962.   set file type \%9
  963.   if = \m(_status) 0 {
  964.     hostlog {Send OK, \v(cps) CPS}
  965.     delete \m(_usertree)/\m(_username).MSG
  966.   } else {
  967.     hostlog {Send failed}
  968. }
  969. :MSG_5
  970. goto main
  971.  
  972. :NOMSGS
  973. atp 4 \%C {No messages for \m(_username)}
  974. sleep 2
  975. goto main
  976.  
  977. :LBL_10 ; LEAVE A MESSAGE
  978. def _inmail 1
  979. def _ctrlc (Ctrl-C to cancel message)
  980. dcl \&a[\m(_msgmax)+5]
  981. def \%k 0 ; Line number of entire message - see addline
  982.  
  983. clr
  984. addline {Date: \v(date) \v(time)}
  985. addline {From: \m(_username) (\m(_myname))}
  986. atp 4 \%C {LEAVE A MESSAGE FOR \m(_owner)}
  987. asg _csave \%C
  988. def \%C 1
  989. def \%L 6
  990. intext \%L {Subject: } 31
  991. if failure goto main
  992. asg _subject \fcontents(\%n)
  993. addline {Subject: \fcontents(\%n)}
  994. addline {}
  995. cur 7 1
  996. cleol
  997. cur 8 1
  998. cleol
  999. out {Type the message now.  To make a blank line, enter a space by itself.}
  1000. cur 9 1
  1001. cleol
  1002. out {Maximum lines: \m(_msgmax).  Type a period by itself on a line to finish.}
  1003. def \%L 10
  1004. cur \%L 1
  1005. cleol
  1006. def \%i 0   ; Line number of message body
  1007. set count \m(_msgmax)
  1008. :MSGLOOP
  1009.   if = \%L 22 newscreen
  1010.   incr \%L
  1011.   incr \%i
  1012.   intext \%L {\flpad(\%i,3,0)> } 31
  1013.   if failure goto main
  1014.   addline {\fcontents(\%n)}
  1015.   if eq "\%n" "." forward msgdone
  1016.   if count goto msgloop
  1017.   atp %\L 1 {Maximum lines exceeded}
  1018. :MSGDONE
  1019. incr \%L
  1020. cur \%L 1
  1021. cleol
  1022. incr \%L
  1023. if > \%L 22 newscreen
  1024. asg \%i 999
  1025.  
  1026. :MSGCFM ; Confirm the message
  1027.   atp \%L 1 {Type Ctrl-C to cancel or Press space bar to send: }
  1028.   input \m(_inactivity)
  1029.   if failure bail
  1030.   asg \%i \fcode(\v(inchar))
  1031.   if = \%i 3 goto main
  1032.   if = \%i 32 forward MSGSEND
  1033.   goto MSGCFM
  1034.  
  1035. :MSGSEND ; Send the message
  1036. open append \m(_msgfile)
  1037. if failure error {Sorry - messages not available now}
  1038. for \%i 1 \%k 1 {
  1039.   writeln file \fcontents(\&a[\%i])
  1040. }
  1041. close append
  1042. asg _status \v(status)
  1043. if = \m(_status) 0 forward MSGOK
  1044. hostlog {Problem sending message}
  1045. msg {Error sending message}
  1046. sleep 2
  1047. forward MSGEND
  1048.  
  1049. :MSGOK
  1050. boxmsg {Message from \m(_username): \m(_subject)}
  1051.  
  1052. :MSGEND
  1053. asg \%C \m(_csave)
  1054. undef _csave
  1055. undef _inmail
  1056. goto main
  1057.  
  1058. :LBL_11 ; CHANGE PASSWORD
  1059. clr
  1060. asg \%M 2
  1061. ;
  1062. ; Lock the database.
  1063. ;
  1064. hostlog {User \m(_username) changing password...}
  1065. LOCK
  1066. if failure error {Busy - try again later}
  1067. open read \fdef(_userfile)
  1068. if failure error {Error - try again later}
  1069. undef _myrecord
  1070. asg \&u[0] 0
  1071. ;
  1072. ; Read in the entire database because the whole thing must be written
  1073. ; out again when done.  The user's record number is assigned to _myrecord.
  1074. ;
  1075. for \%i 1 \m(_maxusers) 1 {
  1076.   read \&u[\%i]
  1077.   if failure break
  1078.   increment \&u[0]
  1079.   getfields {\&u[\%i]}
  1080.   if eq "\m(U_ID)" "\m(_username)" asg _myrecord \%i
  1081. }
  1082. close read
  1083. if not def _myrecord error {Lookup failure}
  1084. getfields {\&u[\m(_myrecord)]}
  1085.  
  1086. :GETPW
  1087. intext 2 {Old Password:    } 31 \m(_inactivity) *
  1088. if not def \%n goto GETPW
  1089. if not eq "\m(U_PW)" "\f.oox(\%n)" {
  1090.   unlock
  1091.   incr \%M
  1092.   error {Sorry.}
  1093. }
  1094. intext 3 {New Password:    } 31 \m(_inactivity) *
  1095. if failure goto main
  1096. if not def \%n goto GETPW
  1097. asg \%9 \%n
  1098. intext 4 {Retype Password: } 31 \m(_inactivity) *
  1099. if failure goto main
  1100. if eq "\%n" "\%9" forward NEWPWOK
  1101. asg \%M 5
  1102. msg {Passwords do not match - please try again}
  1103. incr \%M
  1104. msg {or use Ctrl-C to return to the main menu.}
  1105. sleep 2
  1106. clr
  1107. goto GETPW
  1108.  
  1109. :NEWPWOK
  1110. asg \&u[\m(_myrecord)] -
  1111. \m(U_ID)_\f.oox(\%n)_\m(U_PR)_\m(U_NM)_\m(U_AD)_\m(U_TP)_\m(U_EM)
  1112. saveuserdb
  1113. if success hostlog {Password changed} 
  1114. else error {FAILED - Password not changed}
  1115. sleep 2
  1116. goto main
  1117.  
  1118. :LBL_12 ; HELP
  1119. clr
  1120. if not exist \m(_helpfile) error {Sorry - no help available}
  1121. display \m(_helpfile)
  1122. goto main
  1123.  
  1124. :LBL_13 ; EXIT
  1125. clr
  1126. hostlog {Close transaction log}
  1127. close transact
  1128. def HOSTLOG echo \v(time) - \%1
  1129. hostlog {Exit host mode}
  1130. atp 2 2 Bye!
  1131. cur 3 1
  1132. cd \m(_startdir)
  1133. hangup
  1134. ;close connection
  1135. end 0
  1136.  
  1137. :LBL_14 ; DOS command
  1138. clr
  1139. if not > \m(_mypriv) 1 error {Sorry - insufficient privilege}
  1140. atp  2 \%C {EXECUTE A DOS COMMAND}
  1141. atp  4 \%C {Current directory: \freplace(\v(dir),\\,/)}
  1142. atp  6 \%C {CAUTION: Do NOT issue a command that:}
  1143. atp  8 \%C { 1. Might possibly require input from the PC keyboard.}
  1144. atp  9 \%C { 2. Starts a GUI program.}
  1145. atp 10 \%C { 3. Requires any type of interaction at all.}
  1146. atp 12 \%C {If you do, your session will become stuck and you will}
  1147. atp 13 \%C {have to hang up.  Only give commands that print textual}
  1148. atp 14 \%C {information on a DOS screen and then exit immediately.}
  1149. atp 15 \%C {The output will be displayed on your screen unless you}
  1150. atp 16 \%C {redirect it to a file, in which case you can download or}
  1151. atp 17 \%C {type the file afterwards from the main menu.}
  1152. while true {
  1153.   intext 19 {DOS Command: } 31
  1154.   if failure goto main
  1155.   if def \%n break
  1156. }
  1157. hostlog {DOS command "\fcontents(\%n)"}
  1158. if > \findex(>,\%n) 0 forward DOSREDIR
  1159. ;
  1160. ; Command is not redirected so we redirect it to a temp file
  1161. ; and then display the temp file.  Pipes don't work in Windows 95.
  1162. ;
  1163. asg _tmpfile \m(_tmpdir)K_\v(ntime).TMP
  1164. run \fcontents(\%n) > \freplace(\m(_tmpfile),/,\\)
  1165. ; asg _status \v(status)                ; Doesn't work
  1166. asg _status 2                ; Status is unknown
  1167. if exist \m(_tmpfile) {
  1168.   display \m(_tmpfile)
  1169.   delete \m(_tmpfile)
  1170. }
  1171. goto main
  1172.  
  1173. :DOSREDIR ; Command is already redirected, just run it.
  1174. run \fcontents(\%n)
  1175. asg _status 2
  1176. goto main
  1177.  
  1178. :BADEND
  1179. end 1
  1180.  
  1181. ; End of HOST.KSC
  1182.