#!/usr/local/bin/kermit +
;
; ALPHAPAGE 6.0 for C-Kermit 8.0.201, 206, or 209 (but not 208)
; F. da Cruz, The Kermit Project, Columbia University
;
; TAP/IXO alphanumeric paging script for C-Kermit.
; For a detailed explanation, consult "Using C-Kermit", 2nd Ed., pp.454-456.
;
if LLT \v(version) 800201 stop 1 C-Kermit 8.0.201 or later required.
if EQU \v(version) 800208 stop 1 -
"Sorry - This script doesn't work in 8.0.208; please use 201, 206, or 209.

; Set defaults here.

define d_LINE /dev/ttyb           ; Device modem is attached to

; The following are critical - the script won't work unless
; all of them are set appropriately for the specific paging service,
; Verizon PCS in this example.

define d_APAGEPHONE 18668230501   ; Paging service phone number (VERIZON PCS)
define d_MODEM usrobotics         ; Actual specific modem type
define d_SPEED 1200               ; Serial port speed
define d_PARITY EVEN              ; Parity (usually EVEN or NONE)
define d_FLOW NONE                ; Flow control
define d_PACING 0                 ; Output character pacing (milliseconds)

; Other defaults

define d_REDIALS 20               ; Allow 20 redials (IF LEGAL!)
define d_TESTING 0                ; Set to 1 or 2 for debugging

; You can override defaults with Kermit variables or environment variables:

if not def APAGEPHONE assign APAGEPHONE \$(APAGEPHONE)
if not def APAGEPHONE asg APAGEPHONE \m(d_APAGEPHONE)
if not def LINE assign LINE \$(LINE)
if not def LINE asg LINE \m(d_LINE)
if not def SPEED asg SPEED \$(SPEED)
if not def SPEED asg SPEED \m(d_SPEED)
if not def MODEM assign MODEM \$(MODEM)
if not def MODEM asg MODEM \m(d_MODEM)
if not def PARITY assign PARITY \$(PARITY)
if not def PARITY asg PARITY \m(d_PARITY)
if not def FLOW assign FLOW \$(FLOW)
if not def FLOW asg FLOW \m(d_FLOW)
if not def PACING assign PACING \$(PACING)
if not def PACING asg PACING \m(d_PACING)
;
; WARNING: Automatic redialing is restricted or illegal in some
; countries.  Modify the lines marked "IF LEGAL!" above if this applies
; in your location.
;
assign REDIALS \$(REDIALS)
if not def REDIALS asg REDIALS \m(d_REDIALS)
if not def TESTING {
    assign TESTING \$(TESTING)
    if not def TESTING asg TESTING \m(d_TESTING)
    if not def TESTING asg TESTING 0
    if not numeric \m(TESTING) asg TESTING 1
}

; TAPMSG, defined below, is an alphanumeric pager dialing script that
; implements the Telocator Alphanumeric Protocol (TAP), also known as IXO,
; for sending one-line alphanumeric pages.  TAPMSG assumes the connection
; to the paging service is already made.
;
; The APAGE macro makes a connection to the paging service using the
; device, modem, and phone number definitions at the top of this file,
; and then invokes TAPMSG to send the page.
;
; INSTRUCTIONS
;
; Edit the defaults at the top of this file as needed.
;
; Change the top line to indicate the full pathname of the C-Kermit 8.0
; executable if necessary, then store this file in your PATH with a name like
; "verizon", give it execute permission, and then you can run it by name from
; the shell prompt:
;
;   verizon pagerid message
;
; Command-line args containing spaces must be quoted.  Example:
;
;   verizon 9876543 "This is a message"
; 
; You can also pass the pagerid and message as environment variables instead
; of command-line arguments:
;
;   export PIN=19177654321
;   export MESSAGE="Everything is broken - come and fix!"
;   verizon
;
; To use different paging services, make copies of this file with the
; appropriate names and configuration changes, e.g. skytel with skytel's
; phone number, pagenet, whatever.
;
; EDIT HISTORY
;
; Version 1.0: Sept 1996, for C-Kermit 6.0.
; Version 2.0: July 1997:
;  1. To make TAPMSG fit into 1K for small C-Kermit versions.
;  2. Change END to STOP within SWITCH to work around C-Kermit bug.
; Version 3.0: September 1997, for C-Kermit 7.0 Beta.
;  1. Change STOP back to END because bug is fixed.
;  2. Added robustness.
; Version 4.0: August 1998:
;  1. Additional robustness - requires 2K command buffer.
;  2. Diagnostics.
; Version 5.0: May 2000, for C-Kermit 7.0:
;  1. Improved portability, parameterization, and error checking.
; Version 6.0: Mar 2003, for C-Kermit 8.0:
;  1. More debugging.
;  2. Accept environment variables as well as command-line args.
;  3. Parameterize output pacing -- it can make all the difference.
;
if \m(TESTING) {		 ; Set up debugging messages and logs
    set quiet off
    echo TESTING=\m(TESTING)
    echo COMMAND FILE: \fpathname(\v(cmdfile))
    echo EXEDIR: \v(exedir) ; where Kermit is running from
    set dial display on          ; Watch computer/modem dialog
    set command byte 7           ; Filter out the 8th bit when debugging
    if > \m(TESTING) 1 {         ; serious debugging
        log session              ; Creates session.log in current directory
        log debug                ; Creates debug.log in current directory
        set input echo on        ; Watch input (can mess up your screen)
    }
}

; APSETUP - Set up dialing device and modem.

define APSETUP {                 ; Set up the calling parameters.
    set exit warning off         ; No warnings on exit.
    if eq \v(system) WIN32 {     ; In Windows use the default TAPI device.
        set port tapi          
        if fail end 1
    } else {                     ; Elsewhere...
        set modem type \m(MODEM) ; Specify the modem type.
        if fail end 1 Bad modem type: "\m(MODEM)"
        if eq "\v(modem)" "multitech" {
            set modem command init ATQ0E1X4&E8&Q0&S0$BA0$MB\m(speed)\13
            set modem command error-correction off AT&E0\13
        }
        set line \m(LINE)        ; Specify dialout port.
	if fail end 1 Can't SET LINE to "\m(LINE)"
    }
    if \m(TESTING) {
        echo "PHONE:   \m(APAGEPHONE)"
        echo "PORT:    \m(LINE)"
        echo "MODEM:   \m(MODEM)"
        echo "SPEED:   \m(SPEED)"
        echo "PARITY:  \m(PARITY)"
        echo "FLOW:    \m(FLOW)"
        echo "PACING:  \m(PACING)"
    }
    set term byte 7              ; Ignore the 8th bit - TAP is ASCII only
    set input echo off           ; (this can confuse the terminal)
    set speed \m(SPEED)          ; Speed required by paging service
    if fail stop 1 Bad SPEED - "\m(SPEED)"
    set parity \m(PARITY)        ; Parity required by paging service
    if fail stop 1 Bad PARITY - "\m(PARITY)"
    set flow \m(FLOW)            ; Flow control (usually NONE)
    if fail stop 1 Bad FLOW-CONTROL - "\m(FLOW)"
    set dial interval 1          ; 1 sec between redial attempts
    set dial retries \m(REDIALS)
    if fail stop 1 Bad DIAL RETRIES - "\m(REDIALS)"
    if \m(TESTING) show comm     ; Let's see the modem signals
}

; APAGE - Make the call and send the page.
;   \%1 = Pager ID (PIN)
;   \%2 = Message (single line)
;
define APAGE {                   ; Call the paging service and send a page.
    local rc t                   ; rc = Return code for this macro
    if < \v(argc) 2 end 1 Pager ID required
    if \m(TESTING) {
        asg t \v(ntime)
        echo
        echo "PIN:     \%1"
        echo "MESSAGE: \%2"
    }
    do apsetup                   ; Make the call.
    if fail end 1                ; Quit if the call failed.
    set modem error-correction off  ; Turn OFF error correction
    set modem data-compression off  ; and data compression
    set modem speed-matching on     ; and speed-buffering.
    if def PACING {                 ; Intercharacter pacing for OUTPUT.
        if numeric \m(PACING) set output pacing \m(PACING)
    }
    dial \m(APAGEPHONE)          ; Call the pager service
    asg rc \v(status)	         ; Save DIAL status
    if = \m(rc) 0 {              ; If the call is answered
        tapmsg \%1 {\%2}         ; Send the page
        asg rc \v(status)        ; Save the result
    }
    set dial display off         ; Don't display garbage burst on hangup
    if k-95 set line             ; Release the port
    ;; if c-kermit set line

; In UNIX or VMS: Uncomment previous command if running this script in a 
; loop to send multiple pages AND job has a controlling terminal (if there
; is no controlling terminal, e.g. under Cron or Sendmail, the "set line"
; command can fail.

    if \m(TESTING) echo PORT RELEASED
    if not quiet {
	if = \m(rc) 0 {
	    echo PAGE OK
	} else {
	    echo PAGE FAILED
	}
    }
    if \m(TESTING) {
        echo TIME \feval(\v(ntime) - \m(t))
        echo
    }
    ;
    ; For shared in-out ports you might need to reset the port's original
    ; speed here and maybe also send it some kind of reset command like ATZ.
    ;
    set modem type \v(modem)     ; Restore default modem settings
    end \m(rc)                   ; Return
}

dcl \&m[] = "ACK" "NAK" "ID=" "FORCED DISCONNECT"
def \&m[0] "TIMEOUT"

; TAPMSG Telocator Alphanumeric Protocol execution.  Call with:
;   \%1 = Pager ID (PIN)
;   \%2 = Message (single line)
;  Assumes connection is made.  Uses TAP to send PIN and 1-line message.
;
def TAPMSG {
    local \%i \%m \%s blk		; Local variables
    set case on                         ; INPUT is case sensitive
    asg \%m \2\%1\13\%2\13\3		; <STX>ID<CR>msg<CR><ETX>
    asg \%s \fchecksum(\%m)		; Get checksum and make block
    asg blk \%m\fchar(\fmod(\%s/256,16)+48)-
\fchar(\fmod(\%s/16,16)+48)-
\fchar(\fmod(\%s,16)+48)\13		; Checksummed TAP block
    if \m(TESTING) { echo, echo WAITING FOR ID= PROMPT... }
    msleep 500                          ; Wait half a sec...
    for \%i 1 6 1 {			; Try six times to get prompt
	out \13				; Send <CR>
        if fail end 1 OUTPUT I/O ERROR
        if \m(TESTING) echo SENT <CR>(\%i)
	input 2 {ID=}                   ; Look for "ID=" prompt
	if success break
        if not open connection end 1 FATAL - Connection Lost
        if > \v(instatus) 2 end 1 FATAL INPUT ERROR
    }
    if > \%i 6 end 1 {FAILED: NO "ID=" PROMPT (TRIES=\feval(\%i-1))}
    if \m(TESTING) echo {RCVD "ID="}

    for \%i 1 8 1 {			; Send <ESC>PG1, get <ACK>
	msleep 500                      ; First wait half a second
	output \27PG1\13
        if \m(TESTING) echo SENT <ESC>PG1(\%i)
	minput 3 {\6\13} {\21\13} {ID=} {\27\4\13}
        if \m(TESTING) echo RCVD \&m[\v(minput)]
	switch \v(minput) {
	  :0, continue			; Timeout
	  :1, break			; <ACK>
	  :2, continue			; <NAK>
	  :3, continue                  ; ID= again
	  :4, end 1 FORCED DISCONNECT	; Forced disconnect - fatal
	}
	break
    }
    if > \%i 8 end 1
    def \&m[3] "FORCED DISCONNECT"
    def \&m[4] "ERROR INDICATION"
    in 10 \27[p\13			; Wait for go-ahead
    if fail end 1 NO GO-AHEAD		; Didn't get it
    for \%i 1 8 1 {			; Try eight times
	msleep 500
	output \m(blk)			; Send block
        if \m(TESTING) echo SENT MESSAGE (\%i)
	minput 8 {\6\13} {\21\13} {\13\27\4\13} {\30\13}
        if \m(TESTING) echo RCVD \&m[\v(minput)]
	switch \v(minput) {             ; Get response
	  :0, continue                  ; Timeout
	  :1, break                     ; <ACK> - success
	  :2, continue                  ; <NAK> - transmission error
	  :3, end 1 FORCED DISCONNECT
	  :4, echo "ERROR - RETRYING", continue
	}
	out \4\13                       ; Sign off with <EOT>
        if \m(TESTING) echo SENT EOT
	in 8 \27\4\13                   ; Get <ESC><EOT> back
        if \m(TESTING) {
            if fail {
                echo "TIMEOUT (IGNORED)"
            } else {
                echo "RCVD ACK"
            }
        }
	break
    }
    if > \%i 8 end 1 Too many retries
}

; If invoked as a "kerbang" script execute it now.
; Otherwise TAKE'ing this file simply defines the macros so you can
; use them interactively, e.g. 'apage 7654321 "this is a message"'.
;
if kerbang {
    if not def \%1 asg \%1 \$(PIN)
    if not def \%2 asg \%1 \$(MESSAGE)
    if ( not def \%1 || not def \%2 ) exit 1 {usage: \%0 <pagerid> <message>}
    apage {\%1} {\%2}
    exit \v(status)
} else if > \v(argc) 1 {
    apage {\%1} {\%2}
    exit \v(status)
} else {
    echo APAGE <pin> "<message>" (defined)
}

; (End)
