[comp.sources.misc] v03i002: uucp mail for pc's

wswietse@eutrc3.UUCP (Wietse Venema) (04/20/88)

comp.sources.misc: Volume 3, Issue 2
Submitted-By: "Wietse Venema" <wswietse@eutrc3.UUCP>
Archive-Name: pcmail/Part1

These programs turn a pc into a (non-routing) uucp node. The
programs run under MS-DOS and various flavours of UNIX. Porting
to other OSes should not be difficult.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 8)."
# Contents:  README termcap comport.s hsearch.c hsearch.h pager.c
# Wrapped by wietse@eutwc1 on Wed Apr 20 16:45:03 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(2251 characters\)
sed "s/^X//" >README <<'END_OF_README'
XGENERAL
X
XThe pc-mail software provides a single user with  facilities  for
Xcreating,  sending and receiving electronic mail messages via the
Xuucp network. The programs were developed under UNIX but also run
Xwith MS-DOS. Porting to other OSes should be relatively easy.
X
XFor the non-technical user there is a menu-driven shell that  au-
Xtomatically  invokes  various utility programs, e.g. an editor of
Xthe user's choice for editing or  creating  messages,  a  program
Xthat logs in on a UNIX host to exchange files and so on. Any edi-
Xtor that produces clean ASCII files can be used  (wordstar  files
Xare  also handled correctly).  Other facilities: alias data base,
Xbatch-mode operation.
X
XMore technically oriented users will want to avoid  the  interac-
Xtive  shell  and  use  the  mail  data  base and utility programs
Xdirectly. The necessary information can be found in the implemen-
Xtation  documentation. In adition, almost every source file has a
Xbuilt-in manual page.  The  latter  can  be  extracted  with  the
Xsrctoman.sh shell script.
X
XThe programs have been tested under MS-DOS on XT  and  AT  clones
X(MicroSoft  V4  C compiler), and with Microport System-V. For the
Xinteractive shell, a tiny MS-DOS termcap library is provided.  It
Xrequires the ANSI.SYS tty driver (or better) to work sucessfully.
XIn order to run the software under UNIX  (for  testing  purposes)
Xone  needs  a  C  library  with  the  System-V  library functions
X(strtok(), memcpy() et  al.).  Morever,  the  directory  scanning
Xfunctions  only work with file systems that use fixed-size direc-
Xtory entries (i.e. not BSD).
X
XThe programs support the sending and receiving of electronic mail
Xonly;  no  transfer  of  files by name and no message routing. In
Xfact the pc side treats each data file it receives as a mail mes-
Xsage,  irrespective  of  its  actual  destination. The reason for
Xthese limitations are (besides uucp security problems)  that  all
Xfiles  can be sent as mail, and that a pc does not provide multi-
Xuser support anyway.
X
XYou can do anything with the source, but not ask money for it nor
Xremove  references to the original authors. Complaints, feedback,
Xsuggestions are welcome.
X
X	Wietse Venema	uucp:	mcvax!eutrc3!wswietse
X			bitnet:	wswietse@heitue5
END_OF_README
if test 2251 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test ! -d termcap ; then
    echo shar: Creating directory \"termcap\"
    mkdir termcap
fi
if test -f comport.s -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"comport.s\"
else
echo shar: Extracting \"comport.s\" \(20454 characters\)
sed "s/^X//" >comport.s <<'END_OF_comport.s'
Xtitle IBM PC Communications I/O Routines 
X;
X; @(#) comport.asm      Version hoptoad-1.3     87/03/24
X;
X; Orginal code -- Curt Klinsing
X;
X; Changes and updates -- Copyright (c) 1987 Tim Pozar
X; Anyone can use this code for anything, but it is copyright by Tim
X; and you must leave his copyright in the code.
X;
X; ver: 0
X; rev: 2
X; March 13th 1987
X; This code is in a very early stage and should not be let out.
X; Several other extensive functions are planned as well as changes
X; to the current code.
X;
X; 2/20/87 
X;   Changed segment declarations and function names (eg. _function)
X; to fit Microsoft C 4.0 and linker requirements.
X;
X; FUNCTIONS CHANGED/ADDED --
X; set_tty(port_number)
X;   Function to find current settings of the port and set up serial 
X; port for 'baud' and 'lcbyte', and enable DTR.  This will set up the
X; port number base addressed passed to it (eg. 3F8h) and all functions
X; will use this port until the function is used again. (NOT READY FOR USE)
X;
X; reset_tty()
X;   Function to put the port back into the state it was when it was 
X; first found by set_tty().  If set_tty() was not called it will not 
X; change the settings of the port. (NOT READY FOR USE)
X;
X; 3/13/87
X; get_msr()
X;   Function to read (get) the byte located in the Modem Status 
X; Register (3FEh).  The table below describes the byte returned.
X;   bit  description
X;    0   Delta Clear to Send (DCTS)
X;        Indicates that the !CTS input to the chip has changed state
X;        since the last time it was read by the processor.
X;    1   Delta Data Set Ready (DDSR)
X;        Indicates that the !DRS input to the chip has changed since 
X;        last time it was read by the processor.
X;    2   Trailing Edge Ring Indicator (TERI)
X;        Indicates that the !RI input to the chip has changed from
X;        an on (logical 1) to an off (logical 0) condition.
X;    3   Delta Rx Line Signal detect (DRLSD)
X;        Indicates that the !RLSD input to the chip has changed state.
X; NOTE: Whenever bit 0, 1, 2, or 3 is set to a logical 1, a modem status
X;       interrupt is generated.
X;
X;    4   Clear to Send (CTS)
X;        This bit is the complement of the clear to send (!CTS) input.
X;        If bit 4 (LOOP) of the MCR is set to a logical 1, this is 
X;        equivalent to RTS in the MCR.
X;    5   Data Set Ready (DSR)
X;        This bit is the complement of the data set ready (!DSR) input.
X;        If bit 4 (LOOP) of the MCR is set to a logical 1, this is 
X;        equivalent to DTR in the MCR.
X;    6   Ring Indicator (RI)
X;        This bit is the complement of the ring indicator (!RI) input.
X;        If bit 4 (LOOP) of the MCR is set to a logical 1, this is 
X;        equivalent to OUT 1 in the MCR.
X;    7   Receive Line Signal Detect (RLSD).
X;        This bit is the complement of the received line signal detect
X;        (!RLSD) input. If bit 4 (LOOP) of the MCR is set to a logical 1,
X;        this is equivalent to OUT 2 in the MCR.
X;
X;   Currently this driver is set up for COM1 (3f8h).
X;   If you are using the interupt driven buffer, take out the code 
X; that enables the DTR so that it doesn't get raised until the vectors
X; are initilized. 
X;
X; 22/04/1987 W.Z. Venema 
X;       set_tty()       baud rate parameter (values as the "baud" macro below)
X;       uninit_comm()   don't drop DTR when done
X;
X; 19/05/1987 W.Z. Venema
X;       outp_char()     made it check for Xon/Xoff from other system
X
X_TEXT   SEGMENT BYTE PUBLIC 'CODE'
X_TEXT   ENDS
X_DATA   SEGMENT BYTE PUBLIC 'DATA'
X_DATA   ENDS
XCONST   SEGMENT BYTE PUBLIC 'CONST'
XCONST   ENDS
X_BBS    SEGMENT BYTE PUBLIC 'BBS'
X_BBS    ENDS
X
XDGROUP  GROUP   CONST, _BBS, _DATA
X      ASSUME    CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP
X
X_TEXT      SEGMENT
X;
X;A set of Lattice C and MSC callable functions to support
X;interrupt driven character I/O on the  IBM PC. Input
X;is buffered, output is polled.
X;
X;added functions (TMP) --
Xpublic  _set_tty        ;find current settings, and initialize 
X                        ;comm port to 8 bits and set DTR
Xpublic  _reset_tty      ;reset to settings that set_tty() found
Xpublic  _get_msr        ;get MSR byte from port.
X;
X;original functions --
Xpublic  _init_comm      ;initialize the comm port interupts,
Xpublic  _uninit_comm    ;remove initialization,
Xpublic  _set_xoff       ;enable/disable XON/XOFF,
Xpublic  _get_xoff       ;read XON/XOFF state,
Xpublic  _rcvd_xoff      ;returns true if XOFF rcvd,
Xpublic  _sent_xoff      ;true if XOFF sent,
Xpublic  _inp_cnt        ;returns count of rcv chars,
Xpublic  _inp_char       ;get one char from buffer,
Xpublic  _inp_flush      ;flush input buffer,
Xpublic  _outp_char      ;output a character,
X;
X;A better description can be found in the comment
X;block  in each function.
X;
X;       assume  cs:pgroup
X;
XFALSE   EQU     0
XTRUE    EQU     NOT FALSE
X;
XBASE    EQU     03F8H   ;BASE FOR SERIAL BOARD
X;
XLCR     equ     BASE+3  ; Line control register
XIER     equ     BASE+1  ; Interrup Enable Register
XMCR     EQU     BASE+4  ;modem control register
XMDMSTA  EQU     BASE+5  ;line status register
XMDMMSR  EQU     BASE+6  ;modem status register
XMDMBAD  EQU     BASE    ;lsb baud resgister
XEnblDRdy equ    01H     ; enable 'data-ready' interrupt bit
XIntCtlr  EQU    21H     ;OCW 1 FOR 8259 CONTROLLER
XEnblIRQ4 EQU    0EFH    ;Enable COMMUNICATIONS (IRQ4)
Xdataport EQU    BASE    ;transmit/receive data port
XMaskIRQ4 EQU    10H     ;BIT TO DISABLE COMM INTERRUPT (IRQ4)
X
XMDMCD   EQU     80H     ;mask for carrier dectect
XSETBAU  EQU     80H     ;code for Divisor Latch Access Bit
XMDMTBE  EQU     20H     ;8250 tbe flag
XMDMBRK  EQU     40H     ;command code for 8250 break
XLINMOD  EQU     03H     ;line mode=8 bit, no parity
XMDMMOD  EQU     0BH     ;modem mode = DTR and RTS HIGH
XSTOP2   EQU     04H     ;BIT FOR TWO STOP BITS IF BAUD<300
XRS8259  EQU     20H     ;OCW 3 FOR 8259
XRSTINT  EQU     64H     ;SPECIFIC EOI FOR COMM INTERRUPT 
XXOFF    EQU     13H     ;XOFF character
XXON     EQU     11H     ;XON character
X;
X;       MISCELLANEOUS EQUATES
X;
XCR      EQU     13
XLF      EQU     10
XDosCall EQU     33      ;INTERRUPT NUMBER FOR DOS CALL  
XCNSTAT  EQU     11      ;FUNCTION NUMBER FOR CONSOLE STATUS
XCNIN    EQU     1       ;FUNCTION NUMBER FOR CONSOLE INPUT
XBUFSIZ  EQU     512     ;Max NUMBER OF CHARS
XSetIntVect  EQU 25H     ;SET INTERRUPT VECTOR FUNCTION NUMBER
X
X;
X; Communication parameters --
X;
Xbaud    equ     12      ; 1047 =  110 (are you kidding?)
X                        ;  384 =  300
X                        ;   96 = 1200
X                        ;   48 = 2400
X                        ;   24 = 4800
X                        ;   12 = 9600
Xparity  equ     00000b  ;00000 = none
X                        ;01000 = odd
X                        ;11000 = even
Xstopbit equ     000b    ;  000 = 1 bit
X                        ;  100 = 2 bits
Xwordlth equ     11b     ;   10 = 7 bits
X                        ;   11 = 8 bits
Xlcbyte  equ     parity+stopbit+wordlth      ;line control byte
Xdiv_on  equ     80h     ;divisor latch access bit (DLAB)
X
X;
X;       DUMP BUFFER, COUNT AND POINTER.  
X;
XCIRC_BUF DB     BUFSIZ DUP(?)   ;ALLOW 512 MaxIMUM BUFFERED CHARACTERS
XBUF_TOP EQU     $ - 1           ;KEEP TRACK OF THE TOP OF THE BUFFER
XCIRC_TOP DW     BUF_TOP         ;
X;
XCIRC_IN DW      OFFSET CIRC_BUF ;POINTER TO LAST CHAR. PLACED IN BUFFER
XCIRC_CUR DW     OFFSET CIRC_BUF ;POINTER TO NEXT CHAR. TO BE RETRIEVED FROM
X                                ; BUFFER
XCIRC_CT DW      0               ;COUNT OF CHARACTERS USED IN BUFFER
XSNT_XOFF DB     FALSE           ;FLAG TO CHECK IF AN XOFF HAS BEEN SEND
XGOT_XOFF  DB    FALSE           ;FLAG TO CHECK IF AN XOFF HAS BEEN RECEIVED
XSEE_XOFF  DB    FALSE           ;FLAT TO SEE IF WE ARE INTERESTED IN XON/XOFF
X;
X;
X; set_tty()
X;
X_set_tty proc near
X        push    bp
X        mov     bp,sp           ; wzv C calling standard
X;        mov     dx,mcr
X;        in      al,dx           ; get modem parameters
X;        mov     MCR_BYTE,al     ; save them
X        mov     dx,lcr
X;        in      al,dx           ; get line parameters
X;        mov     LCR_BYTE,al     ; save them
X        mov     al,div_on
X        out     dx,al           ; set 8250 for baud rate selection
X        ; can the baud rate divisor be read to save the settings?
X        ; if so, stick the code here.
X        mov     ax,[bp+4]       ; was mov ax,baud /wzv
X        mov     dx,mdmbad
X        out     dx,al           ; low byte divisor
X        mov     al,ah
X        inc     dx
X        out     dx,al           ; high byte divisor
X        mov     dx,lcr
X        mov     al,lcbyte
X        out     dx,al           ; set line control reg.
X        mov     dx,mcr
X        in      al,dx
X        or      al,mdmmod
X        out     dx,al           ; set DTR high
Xflsh:   mov     dx,dataport
X        in      al,dx
X        mov     dx,mdmsta
X        in      al,dx
X        and     al,1
X        jnz     flsh
X      
X        pop     bp
X        ret
X
X_set_tty endp
X
X
X_reset_tty proc near
X        push    bp
X
X        pop     bp
X        ret
X
X_reset_tty endp
X
X_get_msr proc near
X        push    bp
X        push    ds              ; save data segment
X        push    cs
X        pop     ds
X
X        xor     ax,ax
X        mov     dx,MDMMSR
X        in      al,dx
X
X        pop     ds
X        pop     bp
X        ret
X
X_get_msr endp
X
X;
X; set_xoff(flag)         Enable (flag != 0) or disable
X;int flag;              (flag == 0) XON/ XOFF protocol
X;                       for the character input stream.
X;If enabled, an XOFF will be sent when  the buffer
X;reaches 3/4 full. NOTE: an XON will not be sent auto-
X;matically. Your program must do it when it sees
X;the _rcvd_xoff() flag,  and ready for more chars.
X;
X_set_xoff proc near
X        push    bp
X        mov     bp,sp           ; wzv C calling standard
X        PUSH    DS              ;SAVE DATA SEGMENT
X        mov     bx,[bp+4]       ; wzv C calling standard
X        push    cs
X        pop     ds              ; move code seg addr to data seg reg.
X        cmp     bx,0
X        jnz     to_on
X        mov     see_xoff,FALSE
X        jmp     done1
Xto_on:  mov     see_xoff,TRUE
Xdone1:  pop     ds
X        pop     bp
X        ret
X_set_xoff endp
X;
X;flag = get_xoff()      Returns the current setting
X;                       of the XON/ XOFF flag set
X;by set_xoff(), above.
X;
X_get_xoff proc near
X        push    bp
X        push    ds              ; save data reg
X        push    cs
X        pop     ds              ; move code seg addr to data seg reg.
X        xor     ax,ax
X        mov     al,see_xoff
X        pop     ds
X        pop     bp
X        ret
X_get_xoff endp
X;
X;flag = sent_xoff();    Returns true if an XOFF
X;                       character was sent, indicating
X;the receive buffer is  3/4 full.
X;
X_sent_xoff proc  near
X        push    bp
X        push    ds              ; save data reg
X        push    cs
X        pop     ds              ; move code seg addr to data seg reg.
X        xor     ax,ax
X        mov     al,snt_xoff
X        pop     ds
X        pop     bp
X        ret
X_sent_xoff endp
X;
X; rcvd_xoff()            Returns true if an XOFF was
X;                       received; will return false as
X;soon as an XON is received. Does not effect data output,
X;only indicates the above. (Obviously useless for binary
X;data.)
X;
X_rcvd_xoff proc  near
X        push    bp
X        push    ds              ; save data reg
X        push    cs
X        pop     ds              ; move code seg addr to data seg reg.
X        xor     ax,ax
X        mov     al,got_xoff
X        pop     ds              ; restore data reg
X        pop     bp
X        ret
X_rcvd_xoff endp
X;
X;count = inp_cnt()       Returns the number of characters
X;                       available in the input buffer.
X;
X
X_inp_cnt proc near       
X        push    bp
X        push    ds              ; save data segment
X        push    cs
X        pop     ds              ; move code seg addr to data seg reg
X        mov     ax,circ_ct
X        pop     ds
X        pop     bp
X        ret
X_inp_cnt endp
X;
X; inp_flush()    Flush the input buffer.
X;
X_inp_flush proc  near    
X        push    bp
X        push    ds              ; save data reg
X        push    cs
X        pop     ds              ; move code seg addr to data seg reg.
X        mov     bx,offset circ_buf
X        mov     circ_in,bx      
X        mov     circ_cur,bx
X        xor     ax,ax
X        mov     circ_ct,ax
X        pop     ds
X        pop     bp
X        ret
X_inp_flush endp
X
X; --------- Init -----------------------------------
X; Program initialization:
X;   --  Set up vector for RS232 interrupt (0CH)
X;   --  Enbl IRQ4
X;   --  Enbl RS232 interrupt on data ready
X;
X; ---------------------------------------------------
X
X_init_comm proc  near
X        push    bp
X        cli
X
X;  ---- Set up  INT x'0C' for IRQ4
X
X        push    ds
X        push    cs
X        pop     ds              ;cs to ds
X        mov     dx,offset IntHdlr ;relative adddres of interrupt handler
X        mov     al,0cH          ;interrupt number for comm.
X        mov     ah,SetIntVect   ;function number for setting int vector
X        int     DosCall         ;set interrupt in 8086 table
X        pop     ds              ;restore DS
X
X;  ---- Enbl IRQ4 on 8259 interrupt controller
X
X        cli
X
X        in      al,IntCtlr      ; get current masks 
X        and     al,EnblIRQ4     ; Reset IRQ4 mask
X        out     IntCtlr,al      ; And restore to IMR
X
X;  ---   Enbl 8250 data ready interrupt
X
X        mov     dx,LCR          ; DX ==> LCR
X        in      al,dx           ; Reset DLAB for IER access
X        and     al,7FH
X        out     dx,al
X        mov     dx,IER          ; Interrupt Enbl Register
X        mov     al,EnblDRdy     ; Enable 'data-ready' interrupt
X        out     dx,al
X
X;  ---   Enbl OUT2 on 8250
X
X        mov     dx,MCR          ; modem control register        
X        in      al,dx           ; Enable OUT2
X        or      al,08h            ; find out what is in there and
X        out     dx,al            ; enable the DTR
X
X        sti
X
X        pop     bp
X        ret
X_init_comm endp
X;
X; uninit_comm()          Removes the interrupt structure
X;                       installed by _init_comm(). Must be
X;done before passing control to the DOS, else chars received
X;will be stored into the next program loaded!
X;
X_uninit_comm proc near
X        push    bp
X; ---   Disable IRQ4 on 8259
X
X        cli
X        in      al,IntCtlr      ;GET OCW1 FROM 8259
X        or      al,MaskIRQ4     ;DISABLE COMMUNICATIONS INTERRUPT
X        out     IntCtlr,al
X
X; ---   Disable 8250 data ready interrupt
X        
X        mov     dx,LCR          ; DX ==> LCR
X        in      al,dx           ; Reset DLAB for IER access
X        and     al,7FH
X        out     dx,al
X        mov     dx,IER          ; Interrupt Enbl Register
X        mov     al,0            ; Disable all 8250 interrupts
X        out     dx,al
X
X;  ---   Disable OUT2 on 8250
X
X        mov     dx,MCR          ; modem control register        
X        mov     al,1            ; Disable OUT2 /wzv use 1 instead of 0
X        out     dx,al
X
X        sti
X        pop     bp
X        ret
X_uninit_comm endp
X;
X;char  inp_char()      Return a character from the input
X;                      buffer. Assumes you have called
X;inp_cnt() to see if theres any characters to get.
X;
X_inp_char proc near      
X        push    bp
X        push    ds              ; save data reg
X        push    cs
X        pop     ds              ; move code seg addr to data seg reg.
X        mov     bx,circ_cur
X        xor     ax,ax
X        mov     al,[bx]         ;get next char from circ_buf
X        DEC     circ_ct         ;decrement circ_buf COUNT
X        CMP     bx,circ_top     ;ARE WE AT THE TOP OF THE circ_buf?
X        JZ      reset_cur       ;JUMP IF SO
X        INC     bx              ;ELSE, BUMP PTR
X        JMP SHORT upd_cur
Xreset_cur:
X        mov     bx,OFFSET circ_buf      ;RESET circ_in TO BOTTOM OF BUF.
Xupd_cur:
X        mov     circ_cur,bx             ;SAVE NEW PTR
X        xor     cx,cx
X        mov     cl,see_xoff     ;check if interested in xon/xoff
X        cmp     cl,TRUE
X        jnz     clnup2          ;not interested, so goto return
X        cmp     snt_xoff,TRUE   ;have we sent an xoff?
X        jnz     clnup2          ;no, so return
X        cmp     circ_ct,80h     ;yes, so see in buf is now emptying
X        jg      clnup2          ;not empty enuf to send xon, jump to ret
X        mov     snt_xoff,FALSE
X        mov     cl,XON
X        push    ax              ; save char
X        call    comout
X        pop     ax
Xclnup2: pop     DS              ;GET BACK ENTERING DS
X        pop     bp
X        ret
X_inp_char endp
X;
X; outp_char(c)           Output the character to the
X;char c;                serial port. This is not buffered
X;                       or interrupt driven.
X;
X_outp_char proc  near
X        push    bp
X        mov     bp,sp
X	push	ds		; save data segment register /wzv
X	push	cs		; copy code segment register /wzv
X	pop	ds		; to data segment register /wzv
Xw_Xon:  test    got_Xoff,TRUE	; shut up if Xoff received / wzv
X        jnz     w_Xon
X        mov     cl,[bp+4]
X        sti
X        call    comout
X	pop	ds		; restore data segment register / wzv
X        pop     bp
X        ret
X_outp_char endp
X;
X;Local  subroutine: output CL to the port.
X;
Xcomout: mov     dx,MDMSTA       
X        in      al,dx           ; get 8250 status
X        and     al,MDMTBE       ; check for transmitter ready
X        jz      comout          ; jump if not to wait
X        mov     al,cl           ; get char to al
X        mov     dx,dataport     
X        out     dx,al           ; output char to 8251
X        ret
X;
X;       RECEIVE INTERRUPT HANDLER (CHANGED TO PLACE CHARACTERS IN A
X;        CIRCULAR circ_buf AND TO SEND AN XOFF IF THE circ_buf IS MORE THAN
X;        3/4 FULL - S.G.)
X;
XIntHdlr:
X        CLI
X        push    cx
X        push    dx
X        push    bx
X        push    ax
X        push    ds
X        mov     ax,cs           ;get cur code segment
X        mov     ds,ax           ;and set it as data segment
X        mov     bx,circ_in      ;GET circ_buf IN PTR
X        mov     DX,dataport     ;GET DATA PORT NUMBER
X        IN      AL,DX           ;GET RECEIVED CHARACTER
X;       push    ax
X;       push    dx   
X;       xor     ax,ax
X;       xor     dx,dx
X;       mov     dl,al
X;       mov     ah,2
X;       int     DosCall
X;       pop     dx
X;       pop     ax
X        xor     cx,cx
X        mov     cl,see_xoff     ;check if interested in xon/xoff
X        cmp     cl,TRUE
X        jnz     ck_full         ;not interested goto ck if buf full
X        mov     cl,al           ;put char in cl for testing
X        and     cl,7fh          ;turn off any parity bits 
X        cmp     cl,XOFF         ;see if we got an xoff
X        jnz     ck_xon
X        mov     got_Xoff,TRUE   ; code for handling xon/xoff from remote
X        jmp     clnup
Xck_xon: cmp     cl,XON
X        jnz     reg_ch
X        mov     got_Xoff,FALSE
X        jmp     clnup
X;
X;Normal character; not  XON/XOFF, or XON/XOFF disabled.
X;
Xreg_ch: test    snt_Xoff,TRUE   ;SEE IF sentXoff IS SET
X        jnz     ck_full         ;IF SO, DON'T SEND ANOTHER XOFF
X        CMP     circ_ct,(BUFSIZ * 3)/4  ;ALLOW BUF TO BECOME 3/4 FULL BEFORE
X                                        ; SENDING XOFF
X        jb      savch           ;IF IT'S OK, CONTINUE
X        push    ax              ;SAVE CHARACTER
X        mov     CL,XOFF         ;GET XOFF CHARACTER
X        mov     snt_Xoff,TRUE  ;RESET sentXoff
X        call    comout          ; AND SEND IT
X        pop     ax              ;RETRIEVE CHARACTER
X        JMP SHORT savch         ;IF WE'RE HERE, THE circ_buf HAS BUFSIZ-80H
X                                ;  CHARACTERS
Xck_full:
X        CMP     circ_ct,BUFSIZ  ;SEE IF circ_buf ALREADY FULL
X        JZ      clnup           ; JUMP IF SO, DO NOT PLACE CHARACTER IN BFR
Xsavch:                          
X        mov     [bx],AL         ;SAVE NEW CHARACTER IN circ_buf
X        inc     circ_ct         ;BUMP circ_buf COUNT
X        CMP     bx,circ_top     ;ARE WE AT THE TOP OF THE circ_buf?
X        JZ      reset_in        ;JUMP IF SO
X        inc     bx              ;ELSE, BUMP PTR
X        JMP SHORT into_buf
Xreset_in:
X        mov     bx,OFFSET circ_buf      ;RESET circ_in TO BOTTOM OF BUF.
Xinto_buf:
X        mov     circ_in,bx              ;SAVE NEW PTR
Xclnup:
X        mov     AL,RSTINT
X        OUT     RS8259,AL       ;ISSUE SPECIFIC EOI FOR 8259
X        pop     ds              ;GET BACK ENTERING DS
X        pop     ax
X        pop     bx
X        pop     dx
X        pop     cx
X        sti
X        iret
X
X_TEXT   ENDS
X
Xend
X
X
END_OF_comport.s
if test 20454 -ne `wc -c <comport.s`; then
    echo shar: \"comport.s\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f hsearch.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"hsearch.c\"
else
echo shar: Extracting \"hsearch.c\" \(15412 characters\)
sed "s/^X//" >hsearch.c <<'END_OF_hsearch.c'
X#include <ctype.h>		/* for case-insensitive version */
X#include "hsearch.h"		/* join the parts that were broken up */
X
X#define	strcmp	istrcmp		/* for case-insensitive version */
X
Xstatic ELEMENT **Table = NULL;	/* pointer to dynamicly allocated table */
Xstatic int Num_elem = -1;	/* number of elements */
X
Xextern char *calloc();
X
Xextern void hdestroy();
Xextern int hcreate();
Xextern ENTRY *hsearch();
X
Xstatic int hashit();
X
X/*
X * table of first 1900 or so primes, for use in finding the right prime
X * number to be the table size.  this table may be generally useful...
X */
X
Xstatic unsigned short primetab[] = {
X/*
X * comment these out, so that table will always have a minimal size...
X1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
X*/
X73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
X157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
X239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
X331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
X421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
X509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607,
X613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
X709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811,
X821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
X919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019,
X1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097,
X1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201,
X1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
X1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409,
X1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487,
X1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579,
X1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
X1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777,
X1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877,
X1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993,
X1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083,
X2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179,
X2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287,
X2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381,
X2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473,
X2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609,
X2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693,
X2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789,
X2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887,
X2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001,
X3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119,
X3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229,
X3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
X3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457,
X3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541,
X3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637,
X3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739,
X3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853,
X3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947,
X3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073,
X4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177,
X4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273,
X4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409,
X4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517,
X4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639,
X4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733,
X4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871,
X4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969,
X4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077,
X5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189,
X5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309,
X5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431,
X5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521,
X5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651,
X5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743,
X5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851,
X5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981,
X5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091,
X6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211,
X6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311,
X6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397,
X6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553,
X6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673,
X6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781,
X6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883,
X6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991,
X6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121,
X7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237,
X7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369,
X7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507,
X7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589,
X7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699,
X7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
X7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937,
X7949, 7951, 7963, 7993, 8009, 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087,
X8089, 8093, 8101, 8111, 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209,
X8219, 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, 8293, 8297,
X8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387, 8389, 8419, 8423, 8429, 8431,
X8443, 8447, 8461, 8467, 8501, 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573,
X8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, 8681,
X8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, 8747, 8753, 8761, 8779,
X8783, 8803, 8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861, 8863, 8867, 8887,
X8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011,
X9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137,
X9151, 9157, 9161, 9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 9239, 9241,
X9257, 9277, 9281, 9283, 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371,
X9377, 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463,
X9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533, 9539, 9547, 9551, 9587, 9601,
X9613, 9619, 9623, 9629, 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719,
X9721, 9733, 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, 9817,
X9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929,
X9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 10039, 10061, 10067, 10069,
X10079, 10091, 10093, 10099, 10103, 10111, 10133, 10139, 10141, 10151, 10159,
X10163, 10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259,
X10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337,
X10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459,
X10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, 10589,
X10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, 10663, 10667,
X10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781,
X10789, 10799, 10831, 10837, 10847, 10853, 10859, 10861, 10867, 10883, 10889,
X10891, 10903, 10909, 10937, 10939, 10949, 10957, 10973, 10979, 10987, 10993,
X11003, 11027, 11047, 11057, 11059, 11069, 11071, 11083, 11087, 11093, 11113,
X11117, 11119, 11131, 11149, 11159, 11161, 11171, 11173, 11177, 11197, 11213,
X11239, 11243, 11251, 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317,
X11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437,
X11443, 11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527,
X11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, 11677,
X11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, 11779, 11783,
X11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, 11839, 11863, 11867,
X11887, 11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959,
X11969, 11971, 11981, 11987, 12007, 12011, 12037, 12041, 12043, 12049, 12071,
X12073, 12097, 12101, 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161,
X12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251, 12253, 12263, 12269,
X12277, 12281, 12289, 12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379,
X12391, 12401, 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479,
X12487, 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553,
X12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641, 12647,
X12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, 12743, 12757,
X12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, 12841, 12853, 12889,
X12893, 12899, 12907, 12911, 12917, 12919, 12923, 12941, 12953, 12959, 12967,
X12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, 13049,
X13063, 13093, 13099, 13103, 13109, 13121, 13127, 13147, 13151, 13159, 13163,
X13171, 13177, 13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267,
X13291, 13297, 13309, 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397,
X13399, 13411, 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487,
X13499, 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619,
X13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697, 13709,
X13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799,
X13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, 13883, 13901, 13903,
X13907, 13913, 13921, 13931, 13933, 13963, 13967, 13997, 13999, 14009, 14011,
X14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087, 14107, 14143, 14149,
X14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249, 14251, 14281,
X14293, 14303, 14321, 14323, 14327, 14341, 14347, 14369, 14387, 14389, 14401,
X14407, 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489,
X14503, 14519, 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591,
X14593, 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699,
X14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767, 14771,
X14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851, 14867, 14869,
X14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, 14969,
X14983, 15013, 15017, 15031, 15053, 15061, 15073, 15077, 15083, 15091, 15101,
X15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173, 15187, 15193, 15199,
X15217, 15227, 15233, 15241, 15259, 15263, 15269, 15271, 15277, 15287, 15289,
X15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359, 15361, 15373, 15377,
X15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473,
X15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601,
X15607, 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679,
X15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787,
X15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889,
X15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, 15973, 15991, 16001,
X16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087, 16091, 16097,
X16103, 16111, 16127, 16139, 16141, 16183, 16187, 16189, 16193, 16217, 16223,
X16229, 16231, 16249, 16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349,
X16361, 16363, 16369, 16381
X};
X
X/* hcreate --- create a hash table at least howmany big */
X
Xint hcreate (howmany)
Xregister unsigned int howmany;
X{
X	register int i, j;
X
X	/*
X	 * find first prime number >= howmany, and use it for table size
X	 */
X
X	if (Num_elem != -1)	/* already a table out there */
X		hdestroy();	/* remove it */
X
X	j = sizeof (primetab) / sizeof (primetab[0]);
X	for (i = 0; i < j; i++)
X		if (primetab[i] >= howmany)
X			break;
X
X	if (i >= j)	/* howmany bigger than any prime we have, use it */
X		Num_elem = howmany;
X	else
X		Num_elem = primetab[i];
X
X	if ((Table = (ELEMENT **) calloc (Num_elem, sizeof (ELEMENT *))) == NULL)
X		return (0);
X	else
X		return (1);
X}
X
X/* idestroy --- destroy a single element on a chain */
X
Xstatic void idestroy (elem)
XELEMENT *elem;
X{
X	if (elem != NULL)
X	{
X		idestroy (elem->next);
X		free ((char *) elem);
X	}
X}
X
X/* hdestroy --- nuke the existing hash table */
X
Xvoid hdestroy()
X{
X	register unsigned int i;
X
X	if (Table != NULL)
X	{
X		/* free all the chains */
X		for (i = 0; i < Num_elem; i++)
X			idestroy (Table[i]);
X
X		/* now the table itself */
X		free ((char *) Table);
X		Num_elem = -1;
X		Table = NULL;
X	}
X}
X
X/* hsearch --- lookup or enter an item in the hash table */
X
XENTRY *hsearch (entry, action)
XENTRY entry;
XACTION action;
X{
X	ELEMENT e;
X	ELEMENT *ep = NULL;
X	ELEMENT *ep2 = NULL;
X	int index;
X
X	if (Table == NULL)
X		return (NULL);
X
X	index = hashit (entry.key);
X	if (Table[index] == NULL)	/* nothing there */
X	{
X		if (action == FIND)
X			return (NULL);
X		else
X		{
X			/* add it to the table */
X			e.item = entry;
X			e.next = NULL;
X			if ((Table[index] = (ELEMENT *) calloc (1, sizeof (ELEMENT))) == NULL)
X				return (NULL);
X			*Table[index] = e;
X			return (& Table[index]->item);
X		}
X	}
X	else
X	{
X		/* something in bucket, see if already on chain */
X		for (ep = Table[index]; ep != NULL; ep = ep->next)
X		{
X			if (strcmp (ep->item.key, entry.key) == 0)
X			{
X				if (action == ENTER)
X					ep->item.data = entry.data;
X					/* already there, just change data */
X				/* or action was just find it */
X				return (& ep->item);
X			}
X			else
X				ep2 = ep;
X		}
X		/* at this point, item was not in table */
X		/* ep2 points at last element on the list */
X		if (action == ENTER)
X		{
X			if ((ep2->next = (ELEMENT *) calloc (1, sizeof (ELEMENT))) == NULL)
X				return (NULL);
X			ep2->next->item = entry;
X			ep2->next->next = NULL;
X			return (& ep2->next->item);
X		}
X		else
X			return (NULL);
X	}
X	/*NOTREACHED*/
X}
X
X/* hashit --- do the hashing algorithm */
X
X/*
X * algorithm is sum of string elements, plus string length
X * mod table size.
X *
X * made case insensitive for hyphenation program
X */
X
Xstatic int hashit (text)
Xregister char *text;
X{
X	register long int sum = 0;
X	register int i;
X	register int kludge;
X
X	for (i = 0; (kludge = text[i]) != '\0'; i++)
X		sum += (isupper(kludge) ? tolower(kludge) : kludge);
X	sum += i;
X
X	return (sum % Num_elem);
X}
X
END_OF_hsearch.c
if test 15412 -ne `wc -c <hsearch.c`; then
    echo shar: \"hsearch.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f hsearch.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"hsearch.h\"
else
echo shar: Extracting \"hsearch.h\" \(445 characters\)
sed "s/^X//" >hsearch.h <<'END_OF_hsearch.h'
X/* hsearch.c --- PD simple implementation of System V hsearch(3c) routine */
X
X/* 
X* All I (WZV) changed was: 
X* put this part of the original hsearch.c in a separate file
X* make the lookup and hashing algorithms case-insensitive
X*/
X
X#include <stdio.h>
X
Xtypedef struct entry {
X	char *key;
X	char *data;
X	} ENTRY;
X
Xtypedef enum {
X	FIND,
X	ENTER
X	} ACTION;
X
Xtypedef struct element {
X	ENTRY	item;
X	struct element *next;
X	} ELEMENT;
X
XENTRY *hsearch();
END_OF_hsearch.h
if test 445 -ne `wc -c <hsearch.h`; then
    echo shar: \"hsearch.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f pager.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"pager.c\"
else
echo shar: Extracting \"pager.c\" \(14137 characters\)
sed "s/^X//" >pager.c <<'END_OF_pager.c'
X/*++
X/* NAME
X/*      pager 3
X/* SUMMARY
X/*      pager for text files
X/* PROJECT
X/*      pc-mail
X/* PACKAGE
X/*      mailsh
X/* SYNOPSIS
X/*      #include "pager.h"
X/*
X/*      File *open_pager()
X/*
X/*      void close_pager(p)
X/*      File *p;
X/*
X/*      void set_pager(p)
X/*      File *p;
X/*
X/*      void app_pager(p,s)
X/*      File *p;
X/*      char *s;
X/*
X/*      void del_pager(p)
X/*      File *p;
X/*
X/*      void mesg_pager(p,m)
X/*      File *p;
X/*      char *m[];
X/*
X/*      int scan_pager(p,fmt[,args])
X/*      File *p;
X/*
X/*      void sort_pager(p,dir)
X/*      File *p;
X/*
X/*      int cp_pager(path)
X/*      char *path;
X/*
X/*      int pr_pager()
X/*
X/*      int rd_pager(p,path)
X/*      File *p;
X/*      char *path;
X/*
X/*      char *gets_pager();
X/*
X/*      void puts_pager(s);
X/*      char *s;
X/*
X/*      int ds_pager()
X/*
X/*      int pr_pager()
X/*
X/*      int up_pager()
X/*
X/*      int dn_pager()
X/*
X/*      int pu_pager()
X/*
X/*      int pd_pager()
X/* DESCRIPTION
X/*      The pager provides acces to a pager file which is displayed
X/*      on the screen in the middle window. Some functions operate
X/*	on what is called the "current" pager file. All functions 
X/*	have access to the contents of the middle screen window only.
X/*
X/*      open_pager() creates a new (empty) pager file. The return value
X/*      should be used in subsequent accesses to the file. Sets the
X/*      current file.
X/*
X/*      close_pager() releases storage for a pager file. Sets the
X/*      current file to none if that is the one being deleted.
X/*
X/*      app_pager() appends a new line of text to the end of a pager file.
X/*      Sets the current file.
X/*
X/*      del_pager() deletes the line at the current cursor position. Sets the
X/*      current file.
X/*
X/*      mesg_pager() invokes app_pager() to copy a null-terminated array of 
X/*	strings to a pager file. Since it invokes app_pager(), the current
X/*	pager file is set as well. Pager files filled by mesg-pager()
X/*	will not be displayed with an '-- end of display --' line at their end.
X/*
X/*      ins_pager() inserts a line at the current cursor position. Sets the
X/*      current file.
X/*
X/*      scan_pager() takes similar arguments as scanf(3), reads from the 
X/*      line at the current cursor position and returns the number of 
X/*      successfull conversions done. Does not set the current file.
X/*
X/*      sort_pager() sorts a file alphabetically. Sets the current file.
X/*      The dir argument selects the direction of sort (FORW_SORT, BACK_SORT).
X/*
X/*      set_pager() sets the current file.
X/*
X/*      gets_pager() returns a pointer to the current line in the
X/*      current file, or a null pointer is there is none.
X/*
X/*      puts_pager() replaces the current line in the current file.
X/*
X/*      cp_pager() copies the contents of current pager file
X/*      to a normal (external) file. It returns a nonzero status if
X/*      an error occurred (bad path, write error,...).
X/*
X/*      pr_pager() copies the current pager file to the printer.
X/*
X/*      rd_pager() appends a permanent file to the current pager file.
X/*      It returns a nonzero status if an error occurred. Sets the current file.
X/*	rd_pager() reads through the ascf(3) ascii filter, so that it is 
X/*	suitable for viewing word-processor files.
X/*
X/*      up_pager() moves the cursor one line up, if there is one. The
X/*	screen is scrolled when the cursor was at the top of the screen.
X/*
X/*      dn_pager moves the cursor one line down, if there is one. The
X/*	screen is scrolled when the cursor was at the bottom of the screen.
X/*
X/*      pu_pager() displays a page of text that precedes the one on the
X/*      screen, or as much as there is.
X/*
X/*      pd_pager() displays the next page of text, or as much as there is.
X/* FUNCTIONS AND MACROS
X/*      printcl(), printat(), beep(), propen(), prclose(),ascopen(), 
X/*	ascclose(), ascget()
X/* SEE ALSO
X/*      path(3), window(3), window(5), asc(3)
X/* DIAGNOSTICS
X/*      The buzzer makes noise when attempt is made to move the
X/*      cursor beyond the beginning or end of the pager file.
X/*      The program aborts with an error message if references are made
X/*      to the "current file" or "current line" if there is none.
X/* BUGS
X/*      It looks a lot like an editor, but it isn't.
X/*
X/*	No optimization. It just overwrites the whole middle window.
X/* AUTHOR(S)
X/*      W.Z. Venema
X/*      Eindhoven University of Technology
X/*      Department of Mathematics and Computer Science
X/*      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X/* CREATION DATE
X/*      Fri Apr  3 22:06:00 GMT+1:00 1987
X/* LAST MODIFICATION
X/*	Mon Apr  4 23:46:06 MET 1988
X/* VERSION/RELEASE
X/*	1.3
X/*--*/
X
X#include "defs.h"
X#include "window.h"
X#include "pager.h"
X#include "path.h"
X#include "ascf.h"
X
Xhidden File *curfile = NULL;            /* head of the current file */
X
X/* open_pager - create pager file and set current file */
X
Xpublic File *open_pager()
X{
X    register File *p = curfile = (File *) myalloc(sizeof(File));
X
X    p->top = p->curr = p->head = p->last = NULL;
X    p->opts = 0;
X    return(p);
X}
X
X/* close_pager - release memory in a pager file */
X
Xpublic void close_pager(p)
Xregister File *p;
X{
X    register Line *q;
X
X    if (p) {
X	for (q = p->head; q; q = q->next)	/* release lines */
X	    free((char *)q);
X	if (curfile == p)			/* unset current file */
X	    curfile = 0;
X	free((char *)p);			/* release header */
X    }
X}
X
X/* app_pager - append line at end of file and set current file */
X
Xpublic app_pager(p,s)
Xregister File *p;
Xchar *s;
X{
X    register Line *l = (Line *) myalloc(sizeof(Line)+strlen(s));
X
X    if (p->head == 0) {                         /* first line in the file */
X	p->head = p->top = p->curr = l;
X    } else {                                    /* real append */
X	p->last->next = l;
X    }
X    l->next = NULL;                             /* since it is last */
X    l->prev = p->last;                          /* since it is last */
X    p->last = l;                                /* since it is last */
X
X    strcpy(l->line,s);                          /* copy the line */
X
X    curfile = p;                                /* set current file */
X}
X
X/* del_pager - delete line at cursor and set current file (untested!) */
X
Xpublic void del_pager(p)
Xregister File *p;
X{
X    register Line *l = p->curr;
X
X    if (l) {
X	if (l->prev)
X	    l->prev->next = l->next;
X	if (l->next)
X	    l->next->prev = l->prev;
X	if (l == p->head)
X	    p->curr = p->head = l->next;
X	if (l == p->top)
X	    p->curr = p->top  = l->next ? l->next : l->prev;
X	if (l == p->last)
X	    p->curr = p->last = l->prev;
X	if (l == p->curr)
X	    p->curr = l->next;
X	free((char *) l);
X    }
X    curfile = p;
X}
X
X/* scan_pager - read from line at cursor and set current file */
X
X/* VARARGS2 */
X
Xpublic int scan_pager(p,fmt,a1,a2,a3,a4)
XFile *p;
Xchar *fmt;
Xlong a1,a2,a3,a4;
X{
X    return(p && p->curr ? sscanf(p->curr->line,fmt,a1,a2,a3,a4) : 0);
X}
X
X/* set_pager - set the current file; use with care */
X
Xpublic void set_pager(p)
XFile *p;
X{
X    curfile = p;
X}
X
X/*
X* The following functions provide an easy interface to the keyboard
X* interpreter routines. The keyboard driver just associates a key stroke
X* with a function call, does not care what a function does and has
X* almost no facility for passing function arguments.
X* Although the keyboard interpreter manipulates cursor and page coordinates 
X* when the some keys are hit, it never knows which keys affect what
X* the user sees on the screen.
X* That explains why the following functions rely on the above ones
X* for setting the "current file".
X* It may also explain why the above routines do not immediately update
X* the terminal screen, whereas the routines below do.
X*/
X
X/* ds_pager - display a page of the current pager file */
X
Xpublic int ds_pager()
X{
X    static char endline[] = "-- end of display --";
X
X    if (curfile && curfile->curr) {
X	register Line *p;
X	register int k;
X
X	for (p = curfile->top,k = 0; p && k < wsize[MID]; p = p->next)
X	    k += p->llen = printcl(MID,p->lineno = k,p->line);
X	if (k < wsize[MID])
X	    printcl(MID,k++,(curfile->opts & PG_NOEND) ? "" : endline);
X	while (k < wsize[MID])
X	    printcl(MID,k++,"");
X	printat(MID,curfile->curr->lineno,"");
X    } else {
X	register int k;
X
X	printcl(MID,0,(curfile->opts & PG_NOEND) ? "" : endline);
X	for (k = 1; k < wsize[MID]; k++)
X	    printcl(MID,k,"");
X	printat(MID,0,"");
X    }
X    return(0);  /* screen up-to-date */
X}
X
X/* up_pager - up-arrow key hit. check cursor position */
X
Xpublic int up_pager()
X{
X    register Line *p = curfile ? curfile->curr : 0;
X
X    if (p == 0 || p->prev == 0) {
X	beep();
X    } else {
X	if (p->lineno == 0)
X	    pu_pager();
X	printat(MID,(curfile->curr = p->prev)->lineno,"");
X    }
X    return(0);
X}
X
X/* dn_pager - down-arrow key hit. check cursor position */
X
Xpublic int dn_pager()
X{
X    register Line *p = curfile ? curfile->curr : 0;
X
X    if (p == 0 || p->next == 0) {
X	beep();
X    } else {
X	if (p->lineno+p->llen >= wsize[MID])
X	    pd_pager();
X	printat(MID,(curfile->curr = p->next)->lineno,"");
X    }
X    return(0);
X}
X
X/* pu_pager - display preceding page of info */
X
Xpublic int pu_pager()
X{
X    register Line *p;
X    register int k;
X
X    if (curfile && (p = curfile->top) && curfile->top->prev) {
X	for (k = 0; k < wsize[MID] && p; k += p->llen,p = p->prev)
X	    curfile->top = p;
X	curfile->curr = curfile->top;
X	ds_pager();
X    } else {
X	beep();
X    }
X    return(0);
X}
X
X/* pd_pager - display next page of info */
X
Xpublic int pd_pager()
X{
X    register Line *p = curfile ? curfile->top : 0;
X    register int k;
X    register Line *dummy;
X
X    for (k = 0; k < wsize[MID] && p; k += p->llen,p = p->next)
X	dummy = p;
X    if (p) {
X	curfile->curr = curfile->top = dummy;
X	ds_pager();
X    } else {
X	beep();
X    }
X    return(0);
X}
X
X/*
X* The following functions copy permanent files to pager file
X* and vice-versa. There is a limited error detection facility
X* in the form of nonzero return values.
X*/
X
X/* cp_pager - copy current pager file to permanent file */
X
Xpublic int cp_pager(path)
Xchar *path;
X{
X    register FILE *fp;
X
X    if (curfile && (fp = fopen(path,"w"))) {
X	register Line *pp;
X	int err;
X	for (pp = curfile->head; pp; pp = pp->next)
X	    fputs(pp->line,fp),putc('\n',fp);
X	err = ferror(fp);
X	fclose(fp);
X	return(err);
X    } else {
X	return(-1);
X    }
X}
X
X/* pr_pager - print pager file on default printer */
X
Xpublic int pr_pager()
X{
X    register FILE *fp;
X
X    if (curfile && (fp = propen())) {
X	register Line *pp;
X	int err;
X	for (pp = curfile->head; pp; pp = pp->next)
X	    fputs(pp->line,fp),putc('\n',fp);
X	err = ferror(fp);
X	prclose(fp);
X	return(err);
X    } else {
X	return(-1);
X    }
X}
X
X/* rd_pager - copy ordinary file via filter to pager file */
X
Xpublic int rd_pager(p,path)
XFile *p;
Xchar *path;
X{
X    register FILE *fp;
X
X    if (p && (fp = ascopen(path,"r"))) {		/* init the filter */
X	char buf[BUFSIZ];
X	char *cp = buf;
X	register int c;
X	int err;
X
X	while ((c = ascget(fp)) != EOF) {		/* copy to pager file */
X	    if (c == '\n' || cp == buf+BUFSIZ-1) {
X		*cp = 0;
X		app_pager(p,cp = buf);                  /* line by line */
X	    } else {
X		*cp++ = c; 
X	    }
X	}
X	if (cp > buf) {					/* anything left? */
X	    *cp = '\0';
X	    app_pager(p,buf);
X	}
X	err = ferror(fp);				/* check for errors */
X	ascclose(fp);
X	return(err);
X    } else {
X	return(-1);
X    }
X}
X
X/* fwdcmp, revcmp - compare lexical order of lines */
X
Xhidden int fwdcmp(l1,l2)
XLine **l1,**l2;
X{
X    return(strcmp((*l1)->line,(*l2)->line));
X}
X
Xhidden int revcmp(l1,l2)
XLine **l1,**l2;
X{
X    return(strcmp((*l2)->line,(*l1)->line));
X}
X
X/* sort_pager - sort a pager file, a boring thing */
X
Xpublic void sort_pager(pp,dir)
XFile *pp;
Xint dir;
X{
X    register Line *l;
X    register int i;
X    int lines;
X    Line **lvec;
X
X    for (i = 0,l = pp->head; l; l = l->next)            /* count nbr of lines */
X	i++;
X
X    if (i <= 1)                                         /* no work */
X	return;
X
X    lvec = (Line **) myalloc((lines = i)*sizeof(*lvec));/* allocate vector */
X
X    for (i = 0,l = pp->head; l; l = l->next)            /* fill vector */
X	lvec[i++] = l;
X
X    qsort((char *) lvec,lines,sizeof(*lvec),dir == FORW_SORT ? fwdcmp : revcmp);
X
X    for (i = 0; i < lines-1; i++)                       /* fix forward links */
X	lvec[i]->next = lvec[i+1];
X    lvec[i]->next = NULL;
X
X    lvec[0]->prev = NULL;                               /* fix backward links */
X    for (i = 1; i < lines; i++)
X	lvec[i]->prev = lvec[i-1];
X
X    pp->head = pp->top = pp->curr = lvec[0];            /* fix file header */
X    pp->last = lvec[lines-1];
X
X    free((char *) lvec);                                /* release vector */
X
X    curfile = pp;                                       /* set current file */
X}
X
X/* gets_pager - return current line in current file */
X
Xpublic char *gets_pager()
X{
X    return(curfile && curfile->curr ? curfile->curr->line : 0);
X}
X
X/* puts_pager - replace line (cleanup this mess) */
X
Xpublic void puts_pager(s)
Xchar *s;
X{
X    if (curfile == 0 || curfile->curr == 0) {   /* no-no if there is no line */
X	fatal("puts_pager: no current file");
X    } else {
X	register Line *old = curfile->curr;     /* get current line */
X	register Line *new = (Line *) myalloc(sizeof(Line)+strlen(s));
X	new->prev = old->prev;                  /* fill it in */
X	new->next = old->next;
X	new->lineno = old->lineno;
X	strcpy(new->line,s);
X	if (new->next)                          /* check next line */
X	    new->next->prev = new;
X	if (new->prev)                          /* check previous line */
X	    new->prev->next = new;
X	if (old == curfile->head)               /* check file head */
X	    curfile->head = new;
X	if (old == curfile->top)                /* check file display */
X	    curfile->top = new;
X	if (old == curfile->last)               /* check file tail */
X	    curfile->last = new;
X	free((char *) curfile->curr);           /* release old line */
X	curfile->curr = new;                    /* set current line */
X    }
X}
X
X/* mesg_pager - copy null-terminated array of strings to pager file */
X
Xpublic void mesg_pager(pp,msg)
Xregister File *pp;
Xregister char **msg;
X{
X    pp->opts |= PG_NOEND;			/* suppress end marker */
X
X    while (*msg)
X	app_pager(pp,*msg++);
X}
END_OF_pager.c
if test 14137 -ne `wc -c <pager.c`; then
    echo shar: \"pager.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 1 \(of 8\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
uucp:	mcvax!eutrc3!wswietse	| Eindhoven University of Technology
bitnet:	wswietse@heithe5	| Dept. of Mathematics and Computer Science
surf:	tuerc5::wswietse	| Eindhoven, The Netherlands.