/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "pg_config.h" IDENT("@(#) ansi.c (Gunther Schadow) 12/19/96"); #include "ansi.h" #include #include #include #include /* Timer defaults */ int astimer_A = 3 /* seconds */; int astimer_D = 5 /* seconds */; int astimer_E = 120 /* seconds */; ASTRM* asattach(int fd, size_t buf_size, int flags) { ASTRM *asp; asp = (ASTRM *)malloc(sizeof(ASTRM)); if(asp != NULL) { if(buf_size == 0) buf_size = AS_BUF_SIZE; asp->_size = buf_size; asp->_base = (char *)malloc(buf_size); asp->_flags = 0; if(asp->_base == NULL) { free(asp); errno = ENOBUFS; return NULL; } asp->_fd = fd; asp->_pfx[0]= 0; asp->_bcc = 0; asp->_ackn = 1; asp->_resp = A_ACK0; asp->_blk = 1; if(asmode(asp, flags, 0) == FAIL) { asdetach(asp, 0); errno = AS_ERRNO(asp); return NULL; } } return asp; } /* asdetach * * Terminate the current interaction and clean up ASTRM structure * Do not close the file descriptor. The parameter `how' tells how * the termination is to be accomplished. If how > 0 and the * direction is AS_OUT finish the message normally and send EOT. * If how == 0 or how > 0 and the direction is AS_IN do a transmission * abort/interrupt. If how < 0 do not do any further handshake. */ result asdetach(ASTRM *asp, int how) { result r = SUCCESS; if(how >= 0) { if(how > 0 && ((asp->_flags & AS_DIR_MASK) == AS_OUT)) r = asflush(asp, AS_LAST); r = asmode(asp, 0, AS_DIR_MASK); } free(asp->_base); free(asp); return r; } result asbuf(ASTRM *asp, size_t buf_size) { if(buf_size != asp->_size) { char *new_base; int delta; if((new_base = (char *)realloc(asp->_base, buf_size)) == NULL) { asp->_errno = errno; syslog(LOG_ERR, "asbuf: realloc: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } delta = new_base - asp->_base; asp->_base = new_base; asp->_end += delta; asp->_poi += delta; asp->_stx += delta; } return SUCCESS; } result asflush(ASTRM *asp, int flags) { if(asp->_flags & AS_OUT) { if(asp->_poi > asp->_base) { asp->_flags |= flags & AS_LAST; return _as_send(asp); } else return SUCCESS; } else { syslog(LOG_ERR, "asflush: can't flush input buffer"); AS_ERROR(asp, AS_EILL); return FAIL; } } /* aspurge * * Purge buffer, waisting everything that might be stored in it. * Prefix and status are also reset as well as the BCC. If the * buffer type is AS_MIXED it is reset to AS_HEADING. Pointers * are set depending on the direction mode as follows: * * AS_IN: * .................................. * BASE^ (BASE + SIZE)^ * STX^ * POI^ * END^ * * AS_OUT: * .................................. * BASE^ (BASE + SIZE)^ * POI^ STX^ * END^ */ void aspurge(ASTRM *asp) { DBG(syslog(LOG_DEBUG, "aspurge: begin 0x%lx", asp->_flags)); switch(asp->_flags & AS_DIR_MASK) { case AS_IN: asp->_poi = asp->_end = asp->_stx = asp->_base; break; case AS_OUT: asp->_poi = asp->_base; asp->_end = asp->_stx = asp->_base + asp->_size; break; } asp->_pfx[0] = 0; asp->_bcc = 0; if((asp->_flags & AS_TYPE_MASK) == AS_MIXED) asp->_flags = ( asp->_flags & ~AS_TEXT ) | AS_HEADING; } void asreset(ASTRM *asp) { DBG(syslog(LOG_DEBUG, "asreset: begin 0x%lx", asp->_flags)); aspurge(asp); asp->_flags = ( asp->_flags & ~AS_RESET_MASK ); asp->_ackn = 1; asp->_resp = A_ACK0; #ifdef WITH_BLK asp->_blk = 1; #endif DBG(syslog(LOG_DEBUG, "asreset: end 0x%08x", asp->_flags)); } int asmode(ASTRM *asp, int flags, int mask) { int old_flags = asp->_flags; int new_flags = ( asp->_flags & ~mask ) | flags; DBG(syslog(LOG_DEBUG, "asmode: begin 0x%lx -> 0x%lx", old_flags, new_flags)); if((new_flags & AS_IN) && (new_flags & AS_OUT)) { syslog(LOG_ERR, "asmode: AS_IN and AS_OUT are mutually exclusive"); AS_ERROR(asp, AS_EILL); return FAIL; } if((old_flags & AS_DIR_MASK) && (old_flags & AS_DIR_MASK) != (new_flags & AS_DIR_MASK)) new_flags &= ~AS_BLOCK; if(!(new_flags & AS_DIR_MASK)) new_flags &= ~AS_RESET_MASK; /* * If a block is to be aborted/discarded */ if((asp->_flags & AS_BLOCK) && !(new_flags & AS_BLOCK)) { switch(asp->_flags & AS_DIR_MASK) { case AS_IN: /* * If a block is to be interrupted, discard the block and * issue a termination interrupt: respond with EOT. */ DBG(syslog(LOG_DEBUG, "asmode: discard current block")); if(_as_discard_block(asp, NULL, A_EOT, FALSE) == FAIL) { int err = AS_ERRNO(asp); switch(err) { case AS_EIO: return FAIL; default: /* if there is an interrupt to follow do it */ if((old_flags & AS_DIR_MASK) != (new_flags & AS_DIR_MASK)) { _as_send_ctrl(asp, NULL, A_EOT, 0); AS_ERROR(asp, err); } return FAIL; } } asp->_flags &= ~AS_DIR_MASK; break; case AS_OUT: /* * If a block is to be aborted, send ENQ or DLE ENQ and * wait for response. */ DBG(syslog(LOG_DEBUG, "asmode: block abort")); if(_as_send_ctrl(asp, NULL, _A_C2T(A_ENQ), 0) == FAIL) return FAIL; if(_as_recv_ack(asp, NULL, 0) == FAIL) { int err = AS_ERRNO(asp); switch(err) { case AS_EIO: return FAIL; default: /* if there is an abort to follow do it */ if((old_flags & AS_DIR_MASK) != (new_flags & AS_DIR_MASK)) { _as_send_ctrl(asp, NULL, A_EOT, 0); AS_ERROR(asp, err); } return FAIL; } } break; } } /* * If current direction mode is to be changed */ if((old_flags & AS_DIR_MASK) != (new_flags & AS_DIR_MASK)) { int ctrl, i; DBG(syslog(LOG_DEBUG, "asmode: change current direction")); /* * If a direction is to be aborted/interrupted, note that the * block discard-interrupt already did it. */ switch(asp->_flags & AS_DIR_MASK) { case AS_IN: /* * Termination Interrupt: respond with EOT if EOT is not to be * read anyway. However, if the control character read is a * start block character we need to discard/interrupt the block. */ /* poll for the next control character */ ctrl = _as_recv_ctrl(asp, NULL, NULL, astimer_D); switch(ctrl) { case FAIL: if(AS_ERRNO(asp) == AS_EIO) return FAIL; case A_EOT: break; case _A_C2T(A_SOH): case _A_C2T(A_STX): asp->_flags |= AS_BLOCK; /* we just entered a block! */ if(_as_discard_block(asp, NULL, A_EOT, FALSE) == FAIL) return FAIL; /* go ahead */ default: DBG(syslog(LOG_DEBUG, "asmode: termination interrupt")); if(_as_send_ctrl(asp, NULL, A_EOT, 0) == FAIL) return FAIL; } break; case AS_OUT: /* Normal termination of the transfer phase * OR * Sending Station Abort * send EOT. */ DBG(syslog(LOG_DEBUG, "asmode: sending station termination")); if(_as_send_ctrl(asp, NULL, A_EOT, 0) == FAIL) return FAIL; break; default: /* * No direction mode, nothing else to do. */; } asp->_flags = (asp->_flags & ~AS_DIR_MASK) | (new_flags & AS_DIR_MASK); asreset(asp); switch(new_flags & AS_DIR_MASK) { case AS_IN: DBG(syslog(LOG_DEBUG, "asmode: waiting for ENQ")); if(_as_recv_enq(asp) == FAIL) return FAIL; if(_as_send_ctrl(asp, NULL, asp->_resp, 0) == FAIL) return FAIL; DBG(syslog(LOG_DEBUG, "asmode: ENQ found, responded")); break; case AS_OUT: i = AS_MAX_RECTRL; while(i-- > 0) { DBG(syslog(LOG_DEBUG, "asmode: sending ENQ")); if(_as_send_ctrl(asp, NULL, A_ENQ, 0) == FAIL) return FAIL; DBG(syslog(LOG_DEBUG, "asmode: receiving ACK")); switch(_as_recv_ack(asp, NULL, asp->_resp)) { case SUCCESS: goto dir_success; case A_NAK: break; case A_ENQ: AS_ERROR(asp, AS_ERINT); return FAIL; case FAIL: if(AS_ERRNO(asp) == AS_ETIMERA) break; else return FAIL; } } syslog(LOG_ERR, "ctrl retry counter expired"); AS_ERROR(asp, AS_ERETRY); return FAIL; default: /* * Direction set to null, nothing else to do. */ DBG(syslog(LOG_DEBUG, "asmode: communication reset")); } dir_success: DBG(syslog(LOG_DEBUG, "asmode: direction successful")); } /* * AS_HEADING only if in AS_OUT mode */ if(new_flags & AS_HEADING) { if(!(new_flags & AS_DIR_MASK)) { syslog(LOG_ERR, "asmode: no direction set"); AS_ERROR(asp, AS_EILL); return FAIL; } if(asp->_flags & AS_TEXT) { syslog(LOG_ERR,"asmode: SOH after STX"); AS_ERROR(asp, AS_ETEXT); return FAIL; } else { asp->_flags |= AS_HEADING; asp->_stx = asp->_base + asp->_size; } } /* * AS_TEXT */ if(new_flags & AS_TEXT) { if(!(new_flags & AS_DIR_MASK)) { syslog(LOG_ERR, "asmode: no direction set"); AS_ERROR(asp, AS_EILL); return FAIL; } switch(new_flags & AS_DIR_MASK) { case AS_OUT: asp->_flags |= AS_TEXT; asp->_stx = asp->_poi; break; case AS_IN: while(! (asp->_flags & (AS_TEXT|AS_LAST))) if(_as_recv(asp) == FAIL) return FAIL; if(asp->_flags & AS_TEXT) asp->_poi = asp->_stx; else { syslog(LOG_WARNING, "AS_TEXT: message has no text part"); AS_ERROR(asp, AS_ENOTEXT); return FAIL; } break; } } if(new_flags & AS_LAST) if(!(new_flags & AS_OUT)) { syslog(LOG_ERR, "asmode: can't set LAST for direction " "other than OUT"); AS_ERROR(asp, AS_EILL); return FAIL; } else asp->_flags |= AS_LAST; asp->_flags &= ~(AS_EXCEPTION | AS_ERROR_MASK); return old_flags; }