home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 3 Comm
/
03-Comm.zip
/
CKPM5X_S.ZIP
/
CKUMYR.C
< prev
next >
Wrap
C/C++ Source or Header
|
1990-02-26
|
8KB
|
204 lines
/* myread.c by Kristoffer Eriksson, ske@pkmab.se, 21 Feb 1990. */
#ifdef MYREAD
/* Private buffer for myread() and it's companions. Not for use by anything
* else. ttflui() is allowed to reset them to initial values. ttchk() is
* allowed to read my_count.
*
* my_item is an index into mybuf[]. Increment it *before* reading mybuf[].
*
* A global parity mask variable could be useful too. We could use it to
* let myread() strip the parity on it's own, in stead of stripping sign
* bits as it does now.
*/
static CHAR mybuf[256]; /* Buffer, including push back */
static int my_count = 0; /* Number of chars still in mybuf */
static int my_item = -1; /* Last index read from mybuf[] */
/* myread() -- Efficient read of one character from communications line.
*
* Uses a private buffer to minimize the number of expensive read() system
* calls. Essentially performs the equivalent of read() of 1 character, which
* is then returned. By reading all available input from the system buffers
* to the private buffer in one chunk, and then working from this buffer, the
* number of system calls is reduced in any case where more than one character
* arrives during the processing of the previous chunk, for instance high
* baud rates or network type connections where input arrives in packets.
* If the time needed for a read() system call approaches the time for more
* than one character to arrive, then this mechanism automatically compensates
* for that by performing bigger read()s less frequently. If the system load
* is high, the same mechanism compensates for that too.
*
* myread() is a macro that returns the next character from the buffer. If the
* buffer is empty, mygetbuf() is called. See mygetbuf() for possible error
* returns.
*
* This should be efficient enough for any one-character-at-a-time loops.
* For even better efficiency you might use memcpy()/bcopy() or such between
* buffers (since they are often better optimized for copying), but it may not
* be worth it if you have to take an extra pass over the buffer to strip
* parity and check for CTRL-C anyway.
*
* Note that if you have been using myread() from another program module, you
* may have some trouble accessing this macro version and the private variables
* it uses. In that case, just add a function in this module, that invokes the
* macro.
*/
#define myread() (--my_count < 0 ? mygetbuf() : 255 & (int)mybuf[++my_item])
/* mygetbuf() -- Fill buffer for myread() and return first character.
*
* This function is what myread() uses when it can't get the next character
* directly from it's buffer. Firstly, it calls a system dependant myfillbuf()
* to read at least one new character into the buffer, and then it returns
* the first character just as myread() would have done. This function also
* is responsible for all error conditions that myread() can indicate.
*
* Returns: When OK => a positive character.
* When EOF or error => -2.
*
* Older myread()s additionally returned -1 to indicate that there was nothing
* to read, upon which the caller would call myread() again until it got
* something. The new myread()/mygetbuf() always gets something. Any program
* that actually depends on the old behaviour will break.
*
* Also sets errno to 9999 on EOF for compatibility with the old myread().
* When ckucon is cleaned up to not depend on that, just remove this action
* from mygetbuf().
*
* You could have mygetbuf() return different codes for EOF and other errors,
* but kermit has currently no use for that. Most programs would just finish
* up in both cases anyway.
*
* The debug() call maybe should be removed for optimum performance.
*/
mygetbuf() {
my_count = myfillbuf();
debug(F101, "myfillbuf read", "", my_count);
if (my_count <= 0) {
if (my_count == 0)
errno = 9999;
return -2;
}
--my_count;
return(255 & (int)mybuf[my_item = 0]);
}
/* Specification: Push back up to one character onto myread()'s queue.
*
* This implementation: Push back characters into mybuf. At least one character
* must have been read through myread() before myunrd() may be used. After
* EOF or read error, again, myunrd() can not be used. Sometimes more than
* one character can be pushed back, but only one character is guaranteed.
* Since a previous myread() must have read it's character out of mybuf[],
* that guarantees that there is space for at least one character. If push
* back was really needed after EOF, a small addition could provide that.
*
* myunrd() is currently not called from anywhere inside kermit...
*/
myunrd(ch) CHAR ch; {
if (my_item >= 0) {
mybuf[my_item--] = ch;
++my_count;
}
}
/* myfillbuf(): System dependant read() into mybuf[], as many chars as possible.
*
* Returns: OK => number of characters read, always more than zero.
* EOF => 0
* Error => -1.
*
* If there is input available in the system's buffers, all of it should be
* read inte mybuf[] and the function return immediately. If no input is
* available, it should wait for a character to arrive, and return with that
* one in mybuf[] as soon as possible. It may wait somewhat past the first
* character, but be aware that any such delay lengthens the packet turnaround
* time during kermit file transfers. Should never return with zero characters
* unless EOF or irrecoverable read error.
*
* Correct functioning depends on the correct tty parameters being used.
* Better control of current parameters is required than may have been the
* case in kermit releases. For instance, O_NDELAY (or equivalent) can no
* longer be sometimes off and sometimes on like it used to, unless a special
* myfillbuf() is written to handle that. Otherwise the ordinary myfillbuf()s
* may think they have come to EOF.
*
* If your system has a facility to direclty perform the functioning of
* myfillbuf(), then use it. If the system can tell you have many characters
* are available in it's buffers, then read that amount (but not less than 1).
* If the system can return a special indication when you try to read without
* anything to read, while allowing you to read all there is when there is
* something, you may loop until there is something to read, but probably that
* is not good for the system load.
*/
#ifdef UXIII
/* This is for System III/V with VMIN>0, VTIME=0 and O_NDELAY off,
* and CLOCAL set any way you like. This way, read() will do exactly
* what is required by myfillbuf(): If there is data in the buffers
* of the O.S., all available data is read into mybuf, up to the size
* of mybuf. If there is none, the first character to arrive is
* awaited and returned.
*/
myfillbuf() {
return read(ttyfd, mybuf, sizeof(mybuf));
}
#else /* UXIII */
#ifdef aegis
/* This is quoted from the old myread(). The semantics seem to be
* alright, but maybe errno would not need to be set even when
* there is no error? I don't know aegis.
*/
myfillbuf() {
int count;
count = ios_$get((short)ttyfd, ios_$cond_opt, mybuf, 256L, st);
errno = EIO;
if (st.all == ios_$get_conditional_failed) /* get at least one */
inbufc = ios_$get((short)ttyfd, 0, mybuf, 1L, st);
if (st.all == ios_$end_of_file)
return(0);
else if (st.all != status_$ok) {
errno = EIO;
return(-1);
}
return(count);
}
#else /* aegis */
#ifdef FIONREAD
/* This is for systems with FIONREAD. FIONREAD returns the number
* of characters available for reading. If none are available, wait
* until something arrives, otherwise return all there is.
*/
myfillbuf() {
long avail;
if (ioctl(ttyfd, FIONREAD, &avail) < 0 || avail == 0)
avail = 1;
return read(ttyfd, mybuf, avail);
}
#else /* FIONREAD */
/* Add other systems here, between #ifdef and #else, e.g. NETCON. */
/* When there is no other possibility, read 1 character at a time. */
myfillbuf() {
return read(ttyfd, mybuf, 1);
}
#endif /* !FIONREAD */
#endif /* !aegis */
#endif /* !UXIII */
#endif /* MYREAD */