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.