diff --git a/src/libraries/libisc-new/src/isc_event.c b/src/libraries/libisc-new/src/isc_event.c new file mode 100644 index 0000000000000000000000000000000000000000..490c8d9572233d81dc18fee1c4075cb54d2ec721 --- /dev/null +++ b/src/libraries/libisc-new/src/isc_event.c @@ -0,0 +1,346 @@ +/* +** isc_event.c definitions of ISC subsystem routines +** +** Copyright (c) 1992 Peter Eriksson and Per Cederqvist of the +** Lysator Academic Computer Association. +** +** history: +** 900403 pen initial coding. +** 900612 pen rewrote the message buffering. +** 901102 pen fixed bug in isc_gethostname(). +** 910303 ceder clean up. Removed everything that is lyskom-specific. +** 910304 pen really removed everything lyskom-specific.. :-) +** 920129 pen added support for "lazy" connect +*/ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <time.h> +#include <sys/types.h> +#include <sys/time.h> +#include <errno.h> +#include <stddef.h> +#include <string.h> + +#include "isc.h" +#include "intern.h" + + + +/* + * External function declarations. + */ +extern void *bzero(void *buf, unsigned length); +extern time_t time(time_t *buf); +extern int select(int setsize, + fd_set *rs, fd_set *rw, fd_set *es, + struct timeval *wait); + + + +IscEvent * +isc_getnextevent(IscMaster *mcb, long timeout) +{ + struct timeval wait; + fd_set read_set; + fd_set write_set; + fd_set error_set; + IscEvent * event; + IscEvent * event2; + IscSessionList * isl; + IscSession * scb; + IscSession * new_scb; + IscMessage * msg; + IscAddress * ia; + int nfds; + int numwaitmsgs; + + + /* Allocate an event */ + ISC_XNEW(event); + event->msg = NULL; + event->session = NULL; + +RETRY: + /* Set up the list of file descriptors to wait on */ + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&error_set); + + /* Number of sessions with queued messages */ + numwaitmsgs = 0; + + /* + * Go thru the list of sessions, add to read_set and handle LOGOUT + */ + for (isl = mcb->sessions; isl != NULL; isl = isl->next) + { + scb = isl->scb; + + if (scb->state == ISC_STATE_CLOSING) + { + /* + ** If we are in state CLOSING, return all outstanding messages + ** before returning event LOGOUT unless fd still open and outgoing + ** messages exists. + */ + event->session = scb; + event->msg = isc_popqueue(scb->rd_msg_q); + if (event->msg) + { + event->event = ISC_EVENT_MESSAGE; + + /* Move the session pointer to the next in the circular queue */ + mcb->sessions = mcb->sessions->next; + + return event; + } + else + if (scb->fd == -1 || !isc_pollqueue(scb->wr_msg_q)) + { + event->event = ISC_EVENT_LOGOUT; + + /* Move the session pointer to the next in the circular queue */ + mcb->sessions = mcb->sessions->next; + + return event; + } + } + + /* + * Add to the write set if in state CONNECTING or RUNNING and there + * are messages in the outgoing queue. + */ + if (scb->state == ISC_STATE_CONNECTING || + (scb->state == ISC_STATE_RUNNING && + (scb->sendindex > 0 || isc_pollqueue(scb->wr_msg_q)))) + FD_SET(scb->fd, &write_set); + + /* + * Add to the read set if state RUNNING or LISTENING + */ + if (scb->state == ISC_STATE_RUNNING || + scb->state == ISC_STATE_LISTENING) + FD_SET(scb->fd, &read_set); + + /* + * Increment the count of sessions with waiting messages + */ + if (isc_pollqueue(scb->rd_msg_q)) + numwaitmsgs++; + + /* + * Terminate at end of circular ring + */ + if (isl->next == mcb->sessions) + break; + } + + /* + * Set up the timeout structure: If there are queued messages, + * do not wait in select, else use user supplied timeout limit + */ + if (numwaitmsgs > 0) + { + wait.tv_sec = 0; + wait.tv_usec = 0; + } + else + { + wait.tv_sec = timeout / 1000; + wait.tv_usec = (timeout % 1000L) * 1000L; + } + + /* Wait for event */ + nfds = select(FD_SETSIZE, &read_set, &write_set, &error_set, &wait); + + /* Error in select? */ + if (nfds < 0) + { + event->event = ISC_EVENT_ERROR; + event->msg = isc_mkstrmsg("Error in select()"); + return event; + } + + /* Timeout? */ + if (nfds == 0 && numwaitmsgs == 0) + { + event->event = ISC_EVENT_TIMEOUT; + return event; + } + + for (isl = mcb->sessions; isl != NULL; isl = isl->next) + { + scb = isl->scb; + + + if (FD_ISSET(scb->fd, &write_set)) + switch (scb->state) + { + case ISC_STATE_RUNNING: + isc_oflush(scb); + break; + + case ISC_STATE_CONNECTING: + ia = isc_getraddress(scb); + if (ia == NULL) + { + /* + * Connect request failed + */ + scb->state = ISC_STATE_CLOSING; + scb->errno = errno; + event->event = ISC_EVENT_REJECTED; + event->session = scb; + } + else + { + /* + * Connect request acknowledged + */ + isc_free(ia); + scb->state = ISC_STATE_RUNNING; + event->event = ISC_EVENT_CONNECTED; + event->session = scb; + } + + /* Move the session pointer to the next in the circular queue */ + mcb->sessions = mcb->sessions->next; + + return event; + + default: + /* Major fuck-up somewhere, this should not happen... */ + event->event = ISC_EVENT_ERROR; + event->session = scb; + event->msg = isc_mkstrmsg( + "Should not happen; illegal state for write FD_SET"); + + /* Move the session pointer to the next in the circular queue */ + mcb->sessions = mcb->sessions->next; + + return event; + } + + if (isl->next == mcb->sessions) + break; + } + + /* Check for messages from sessions */ + for (isl = mcb->sessions; isl != NULL; isl = isl->next) + { + scb = isl->scb; + + if (FD_ISSET(scb->fd, &read_set) && + !(scb->type == ISC_TYPE_TCP && scb->state == ISC_STATE_LISTENING)) + { + time(&scb->idlesince); + + msg = ISC_SCALLFUN1(scb, read, scb); + if (msg) + { + scb->stats.rx.bytes += msg->length; + scb->stats.rx.packets++; + + if (scb->fun.parse) + { + event2 = ISC_SCALLFUN2(scb, parse, scb, msg); + if (event2) + { + isc_free(event); + return event2; + } + } + + isc_pushqueue(scb->rd_msg_q, msg); + } + else + { + scb->state = ISC_STATE_CLOSING; + scb->errno = errno; + } + } + + if (isl->next == mcb->sessions) + break; + } + + + /* New connection? */ + for (isl = mcb->sessions; isl != NULL; isl = isl->next) + { + scb = isl->scb; + + if (scb->state == ISC_STATE_LISTENING && + FD_ISSET(scb->fd, &read_set) && + scb->fun.accept != NULL) + { + new_scb = ISC_SCALLFUN2(scb, accept, scb, NULL); + if (new_scb == NULL) + { + event->event = ISC_EVENT_ERROR; + event->session = NULL; + event->msg = isc_mkstrmsg("Failed isc_getnextevent() " + "in new session"); + return event; + } + + /* Fill in the event info structure */ + event->session = new_scb; + event->event = ISC_EVENT_LOGIN; + + /* Move the session pointer to the next in the circular queue */ + mcb->sessions = mcb->sessions->next; + + isc_insert(mcb, new_scb); + return event; + } + + if (isl->next == mcb->sessions) + break; + } + + /* Return one of the received messages from the clients */ + for (isl = mcb->sessions; isl != NULL; isl = isl->next) + { + scb = isl->scb; + + + if (isc_pollqueue(scb->rd_msg_q)) + { + event->session = scb; + event->msg = isc_popqueue(scb->rd_msg_q); + event->event = ISC_EVENT_MESSAGE; + + /* Move the session pointer to the next in the circular queue */ + mcb->sessions = mcb->sessions->next; + + return event; + } + + if (isl->next == mcb->sessions) + break; + } + + goto RETRY; /* This happens if/when only transmits where pending.. */ +} + + + +/* +** Get rid of the specified event +*/ +void +isc_dispose(IscEvent *ep) +{ + if (!ep) + return; + + if (ep->msg) + isc_freemsg(ep->msg); + + isc_free(ep); +} + + diff --git a/src/libraries/libisc-new/src/isc_master.c b/src/libraries/libisc-new/src/isc_master.c new file mode 100644 index 0000000000000000000000000000000000000000..ccf8ab82f978b7d5e40152a553e79dfa1d0391df --- /dev/null +++ b/src/libraries/libisc-new/src/isc_master.c @@ -0,0 +1,120 @@ +/* +** isc_master.c IscMaster control functions +** +** Copyright (c) 1992 Peter Eriksson and Per Cederqvist of the +** Lysator Academic Computer Association. +** +** history: +** 910305 pen moved to separate file +*/ + +#include <errno.h> +#include <stdlib.h> +#include <stddef.h> +#include <sys/file.h> +#include "isc.h" +#include "intern.h" + + + +/* +** This routine will initialize the ISC subsystem +*/ +IscMaster * +isc_initialize(IscConfig * cfg) +{ + IscMaster * mcb; + IscMasterConfig * mcfg; + IscSessionConfig * scfg; + + + ISC_XNEW(mcb); + + mcb->sessions = NULL; + + + /* Setup default values */ + mcb->scfg.max.msgsize = ISC_DEFAULT_MAX_MSG_SIZE; + mcb->scfg.max.queuedsize = ISC_DEFAULT_MAX_QUEUED_SIZE; + mcb->scfg.max.dequeuelen = ISC_DEFAULT_MAX_DEQUEUE_LEN; + mcb->scfg.max.openretries = ISC_DEFAULT_MAX_OPEN_RETRIES; + mcb->scfg.max.backlog = ISC_DEFAULT_MAX_BACKLOG; + + /* Handle user specified defaults */ + if (cfg) + switch (cfg->version) + { + case 1005: + scfg = &cfg->session; + mcfg = &cfg->master; + + switch (scfg->version) + { + case 0: + break; + + case 1001: + if (scfg->max.msgsize >= 0) + mcb->scfg.max.msgsize = scfg->max.msgsize; + if (scfg->max.queuedsize >= 0) + mcb->scfg.max.queuedsize = scfg->max.queuedsize; + if (scfg->max.dequeuelen >= 0) + mcb->scfg.max.dequeuelen = scfg->max.dequeuelen; + if (scfg->max.openretries >= 0) + mcb->scfg.max.openretries = scfg->max.openretries; + if (scfg->max.backlog >= 0) + mcb->scfg.max.backlog = scfg->max.backlog; + break; + + default: + isc_free(mcb); + errno = EINVAL; + return NULL; + } + + switch (mcfg->version) + { + case 0: + break; + + case 1001: + if (mcfg->memfn.alloc && + mcfg->memfn.realloc && + mcfg->memfn.free) + isc_setallocfn(mcfg->memfn.alloc, + mcfg->memfn.realloc, + mcfg->memfn.free); + if (mcfg->abortfn) + isc_setabortfn(mcfg->abortfn); + break; + + default: + isc_free(mcb); + errno = EINVAL; + return NULL; + } + break; + + default: + isc_free(mcb); + errno = EINVAL; + return NULL; + } + + return mcb; +} + + + +/* +** Close and destroy all sessions associated with a MCB, then +** destroy the MCB too. +*/ +void +isc_shutdown(IscMaster * mcb) +{ + while (mcb->sessions) + isc_destroy(mcb, mcb->sessions->scb); + + isc_free(mcb); +} diff --git a/src/libraries/libisc-new/src/isc_message.c b/src/libraries/libisc-new/src/isc_message.c new file mode 100644 index 0000000000000000000000000000000000000000..e9f9c9bc6a4c2b9a15c4644d431f3690398278af --- /dev/null +++ b/src/libraries/libisc-new/src/isc_message.c @@ -0,0 +1,83 @@ +/* +** isc_message.c Generic "IscMessage" handling functions +** +** Copyright (c) 1991 Peter Eriksson and Per Cederqvist of the +** Lysator Academic Computer Association. +** +** history: +** 910305 pen moved into separate file +** 920102 pen added isc_copymsg() +*/ + +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include "isc.h" +#include "intern.h" + +extern void *memcpy(void *, void *, int); + + +IscMessage * +isc_allocmsg(size_t size) +{ + IscMessage * msg; + + + ISC_XNEW(msg); + msg->buffer = (char *) isc_malloc(size+1); + msg->size = size; + msg->length = 0; + msg->address = NULL; + + return msg; +} + + +IscMessage * +isc_reallocmsg(IscMessage * msg, + size_t newsize) +{ + msg->buffer = (char *) isc_realloc(msg->buffer, newsize+1); + msg->size = newsize; + return msg; +} + + +IscMessage * +isc_copymsg(IscMessage *msg) +{ + IscMessage * newmsg; + + newmsg = isc_allocmsg(msg->size); + if (msg->buffer && msg->length > 0) + memcpy(newmsg->buffer, msg->buffer, msg->length); + newmsg->length = msg->length; + newmsg->address = msg->address; + return newmsg; +} + + +void +isc_freemsg(IscMessage * msg) +{ + if (msg->buffer) + isc_free(msg->buffer); + + isc_free(msg); +} + + + +IscMessage * +isc_mkstrmsg(const char *str) +{ + IscMessage * msg; + + + msg = isc_allocmsg(strlen(str)+1); + strcpy(msg->buffer,str); + msg->length = strlen(str); + + return msg; +} diff --git a/src/libraries/libisc-new/src/isc_output.c b/src/libraries/libisc-new/src/isc_output.c new file mode 100644 index 0000000000000000000000000000000000000000..dff4f4cf769e67668059d72f5220cbfec1c2d892 --- /dev/null +++ b/src/libraries/libisc-new/src/isc_output.c @@ -0,0 +1,175 @@ +/* +** isc_output.c Isc data output functions +** +** Copyright (c) 1991 Peter Eriksson and Per Cederqvist of the +** Lysator Academic Computer Association. +** +** history: +** 910305 pen moved into separate file +** 910310 pen added isc_send() +*/ + +#include <stdlib.h> +#include <stddef.h> +#include <errno.h> +#include <string.h> +#include <sys/file.h> +#include <errno.h> +#include "isc.h" +#include "intern.h" + + +/* + * External function declarations + */ +extern void *memcpy(void *, void *, int); +extern char *strerror(int); + + +void isc_flush(IscSession *scb) +{ + switch (scb->state) + { + case ISC_STATE_CLOSING: + if (scb->wr_msg_q) + { + isc_killqueue(scb->wr_msg_q); + scb->wr_msg_q = NULL; + } + if (scb->rd_msg_q) + { + isc_killqueue(scb->rd_msg_q); + scb->rd_msg_q = NULL; + } + scb->sendindex = 0; + return; + + case ISC_STATE_RUNNING: + case ISC_STATE_CONNECTING: + isc_oflush(scb); + return; + + default: + return; + } +} + + + +void isc_oflush(IscSession *scb) +{ + IscMessage * msg; + int failed; + int wlen; + int loopcnt; + + + /* Data in the block buffer? */ + if (scb->sendindex > 0) + { + /* Too many queued blocks? */ + if (isc_sizequeue(scb->wr_msg_q) >= scb->cfg->max.queuedsize) + { + scb->state = ISC_STATE_CLOSING; + scb->errno = E2BIG; + scb->sendindex = 0; /* And lets fake a write */ + return; + } + + msg = isc_allocmsg(scb->sendindex); + memcpy(msg->buffer, scb->sendbuf, scb->sendindex); + msg->length = scb->sendindex; + isc_pushqueue(scb->wr_msg_q, msg); + scb->sendindex = 0; + } + + /* We only try to transmit messages for RUNNING or CLOSING sessions */ + if ((scb->state != ISC_STATE_RUNNING && + scb->state != ISC_STATE_CLOSING) || scb->fd == -1) + return; + + /* Queued entries? Send as much as possible */ + failed = 0; + loopcnt = 0; + while ((msg = isc_topqueue(scb->wr_msg_q)) != NULL && + !failed && loopcnt < scb->cfg->max.dequeuelen) + { + wlen = ISC_SCALLFUN2(scb, write, scb, msg); + + if (wlen < 0) + { + switch ( errno ) + { + case EWOULDBLOCK: + wlen = 0; + break; + + case EPIPE: + scb->state = ISC_STATE_CLOSING; + scb->errno = errno; + return; + + default: + scb->state = ISC_STATE_CLOSING; + scb->errno = errno; + scb->sendindex = 0; + return; + } + } + + if (wlen > 0) + { + scb->stats.tx.bytes += wlen; + scb->stats.tx.packets++; + } + + msg->length -= wlen; + if (msg->length > 0) + { + /* The write handler could not transmit the whole buffer */ + failed = 1; + if (wlen > 0) + memcpy(msg->buffer, msg->buffer + wlen, msg->length); + } + else + { + /* Drop the topmost entry */ + msg = isc_popqueue(scb->wr_msg_q); + isc_freemsg(msg); + } + + loopcnt++; + } +} + + + +/* +** Put an IscMessage onto an IscSession transmit queue +*/ +int +isc_send(IscSession *scb, IscMessage *msg) +{ + /* Must flush if pending "stdout" output */ + if (scb->sendindex) + isc_flush(scb); + + isc_pushqueue(scb->wr_msg_q, msg); + return 0; +} + + + +int +isc_sendto(IscSession *scb, + IscAddress *ia, + IscMessage *msg) +{ + if (scb->type != ISC_TYPE_UDP) + return -1; + + msg->address = ia; + + return isc_send(scb, msg); +} + diff --git a/src/libraries/libisc-new/src/isc_queue.c b/src/libraries/libisc-new/src/isc_queue.c new file mode 100644 index 0000000000000000000000000000000000000000..e2521a0b3de25559de54e6414dfa5c032740b5dc --- /dev/null +++ b/src/libraries/libisc-new/src/isc_queue.c @@ -0,0 +1,146 @@ +/* +** isc_queue.c Handle queues for the ISC subsystem +** +** Copyright (c) 1991 Peter Eriksson and Per Cederqvist of the +** Lysator Academic Computer Association. +** +** history: +** 910305 pen moved to separate file +*/ + +#include <stdlib.h> +#include <stddef.h> +#include "isc.h" +#include "intern.h" + + +IscMsgQueue * +isc_newqueue(void) +{ + IscMsgQueue * msg_q; + + + msg_q = (IscMsgQueue *) isc_malloc(sizeof(IscMsgQueue)); + msg_q->head = NULL; + msg_q->tail = NULL; + msg_q->entries = 0; + + return msg_q; +} + + +/* Return TRUE if the queue is not empty. */ +int +isc_pollqueue(IscMsgQueue * queue) +{ + return queue != NULL && queue->head != NULL; +} + + +void +isc_killqueue(IscMsgQueue * queue) +{ + IscMsgQE * mqe; + IscMsgQE * prev; + + + if (queue == NULL) + return; + + if ( queue->head != NULL ) + { + mqe = queue->head; + while ( mqe != NULL ) + { + prev = mqe; + mqe = mqe->next; + isc_freemsg(prev->msg); + isc_free(prev); + } + } + + isc_free(queue); + return; +} + + + +/* +** Push a message onto a queue. It is legal to push a message onto the +** the NULL queue and will be equal to freeing that message +*/ +void +isc_pushqueue(IscMsgQueue * queue, + IscMessage * msg) +{ + IscMsgQE * mqe; + + + if (queue == NULL) + { + isc_freemsg(msg); + return; + } + + mqe = (IscMsgQE *) isc_malloc(sizeof(IscMsgQE)); + + mqe->msg = msg; + mqe->prev = queue->tail; + mqe->next = NULL; + + if (queue->head == NULL) + queue->head = mqe; + + if (queue->tail) + queue->tail->next = mqe; + + queue->tail = mqe; + queue->entries++; +} + + +IscMessage * +isc_topqueue(IscMsgQueue * queue) +{ + if (queue != NULL && queue->head != NULL) + return queue->head->msg; + else + return NULL; +} + + +int +isc_sizequeue(IscMsgQueue * queue) +{ + if (queue) + return queue->entries; + else + return -1; +} + + +IscMessage * +isc_popqueue(IscMsgQueue * queue) +{ + IscMsgQE * mqe; + IscMessage * msg; + + + if (queue != NULL && queue->head != NULL) + { + mqe = queue->head; + msg = mqe->msg; + queue->head = mqe->next; + + if (queue->head) + queue->head->prev = NULL; + else + queue->tail = NULL; + + queue->entries--; + isc_free(mqe); + return msg; + } + else + return NULL; +} diff --git a/src/libraries/libisc-new/src/isc_session.c b/src/libraries/libisc-new/src/isc_session.c new file mode 100644 index 0000000000000000000000000000000000000000..639b53f287649b266fb83e38afe7171eb2685b39 --- /dev/null +++ b/src/libraries/libisc-new/src/isc_session.c @@ -0,0 +1,446 @@ +/* +** isc_session.c Routines to handle ISC sessions +** +** Copyright (c) 1992 Peter Eriksson and Per Cederqvist of the +** Lysator Academic Computer Association. +** +** history: +** 910305 pen moved into separate file +** 920129 pen added support for "lazy" connect() +** 920209 pen reworked some of the code +** 920209 pen TCP and UDP specific code removed. +*/ + +#include <sys/types.h> +#include <errno.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <fcntl.h> + +#include "isc.h" +#include "intern.h" + + + +/* +** External function declarations +*/ +extern void *memset(void *, int, int); +extern int close(int); +extern time_t time(time_t *); +extern int read(int fd, char *buf, int len); +extern int write(int, char *, int); + + + +/* +** ISC-Internal default function to read data +*/ +IscMessage *isc_default_read_fn(IscHandlerList *hl, IscSession *scb) +{ + int status; + IscMessage *msg; + + + msg = isc_allocmsg(scb->cfg->max.msgsize); + + status = read(scb->fd, msg->buffer, msg->size); + if (status <= 0) + { + status = errno; + isc_freemsg(msg); + errno = status; + return NULL; + } + + msg->length = status; + msg->buffer[msg->length] = '\0'; + + return msg; +} + + + +int isc_default_write_fn(IscHandlerList *hl, + IscSession *scb, + IscMessage *msg) +{ + return write(scb->fd, msg->buffer, msg->length); +} + + + +int isc_default_close_fn(IscHandlerList *hl, + IscSession *scb) +{ + int code = 0; + + + if (scb->fd != -1) + { + code = close(scb->fd); + scb->fd = -1; + } + + return code; +} + + + +/* +** Insert a session into a master control structure +*/ +int +isc_insert(IscMaster *mcb, + IscSession *scb) +{ + IscSessionList *isl; + + + ISC_XNEW(isl); + isl->scb = scb; + + /* Better check if this is the first session or not */ + if (mcb->sessions != NULL) + { + isl->next = mcb->sessions; + isl->prev = mcb->sessions->prev; + isl->prev->next = isl; + mcb->sessions->prev = isl; + } + else + { + isl->next = isl; + isl->prev = isl; + } + + mcb->sessions = isl; + + return ++scb->nlinks; +} + + + +/* +** Locate a session list entry in a MCB +*/ +static IscSessionList * +isc_findsession(IscMaster *mcb, + IscSession *scb) +{ + IscSessionList *isl; + + + /* Locate the session in the MCB */ + for (isl = mcb->sessions; isl != NULL && isl->scb != scb; isl = isl->next) + if (isl->next == mcb->sessions) + break; + + /* Not found? Return -1 */ + if (isl == NULL || isl->scb != scb) + return NULL; + + return isl; +} + + + +/* +** Remove a session from a master control structure +*/ +int +isc_remove(IscMaster *mcb, + IscSession *scb) +{ + IscSessionList *isl; + + + isl = isc_findsession(mcb, scb); + if (!isl) + { + errno = ENOENT; + return -1; + } + + /* Remove it from the list of sessions if it is still on it */ + if (isl->prev != NULL) + { + if (isl->next != isl) + { + /* Make sure noone references this session */ + if (mcb->sessions == isl) + mcb->sessions = isl->next; + + isl->prev->next = isl->next; + isl->next->prev = isl->prev; + } + else + { + /* This was the last session on the circular list. */ + mcb->sessions = NULL; + } + + isl->prev = NULL; + isl->next = NULL; + } + + return --scb->nlinks; +} + + +static IscHandler isc_default_fun = +{ + &isc_default_read_fn, + &isc_default_write_fn, + &isc_default_close_fn, + NULL, + NULL, + NULL, + NULL +}; + + + +/* +** Create a new session structure. +*/ +IscSession * +isc_create(IscSessionConfig *cfg, IscHandler *fun) +{ + IscSession * scb; + + + ISC_XNEW(scb); + + scb->nlinks = 0; + scb->fd = -1; + scb->type = ISC_TYPE_UNKNOWN; + scb->state = ISC_STATE_UNKNOWN; + scb->errno = 0; + scb->rd_msg_q = NULL; + scb->wr_msg_q = NULL; + scb->handlers = NULL; + + memset(&scb->fun, 0, sizeof(scb->fun)); + + scb->cfg = cfg; + + scb->stats.rx.bytes = 0; + scb->stats.rx.packets = 0; + scb->stats.tx.bytes = 0; + scb->stats.tx.packets = 0; + + /* Fill in the session structure */ + scb->rd_msg_q = isc_newqueue(); + scb->wr_msg_q = isc_newqueue(); + scb->sendindex = 0; + + if (!fun) + fun = &isc_default_fun; + + isc_pushhandler(scb, fun); + + return scb; +} + + + +/* +** Destroy the allocated Session structure. Close the +** socket if open and if Master structure specified, remove +** the session from it. +*/ +int +isc_destroy(IscMaster *mcb, + IscSession *scb) +{ + int code = 0; + + + if (mcb) + { + code = isc_remove(mcb, scb); + if (code < 0) + return code; + } + + + /* + ** Only close and deallocate storage if last reference has been + ** removed. + */ + if (scb->nlinks <= 0) + { + if (scb->fun.destroy) + ISC_SCALLFUN1(scb, destroy, scb); + + if (scb->fd != -1) + { + close(scb->fd); + scb->fd = -1; + } + + if (scb->rd_msg_q) + isc_killqueue(scb->rd_msg_q); + + if (scb->wr_msg_q) + isc_killqueue(scb->wr_msg_q); + + isc_free(scb); + } + + return code; +} + + + +/* +** Put the session into CLOSING state, this also kills the queue of incoming +** messages. +*/ +void +isc_close(IscSession *scb) +{ + isc_flush(scb); + scb->state = ISC_STATE_CLOSING; + if (scb->rd_msg_q) + { + isc_killqueue(scb->rd_msg_q); + scb->rd_msg_q = NULL; + } +} + + + +/* +** Return the number of active sessions +*/ +int +isc_sessions(IscMaster * mcb) +{ + IscSessionList * isl; + IscSessionList * top_isl; + int cnt; + + + top_isl = mcb->sessions; + for (cnt = 0, isl = top_isl; isl; cnt++, isl = isl->next) + if (isl->next == top_isl) + break; + + return cnt; +} + + + +/* +** Create a new session, insert it into a master structure +** and associate a file descriptor with it. +*/ +IscSession * +isc_openfd(IscMaster *mcb, + int fd) +{ + IscSession * scb; + int res; + + + /* Set non blocking write mode */ + if ((res = fcntl(fd, F_GETFL, 0)) == -1) + { + close(fd); + return NULL; + } + + if (fcntl(fd, F_SETFL, res | FNDELAY) == -1) + { + close(fd); + return NULL; + } + + scb = isc_create(&mcb->scfg, NULL); + if (!scb) + return NULL; + + time(&scb->logintime); + + scb->state = ISC_STATE_RUNNING; + scb->fd = fd; + + (void) isc_insert(mcb, scb); + return scb; +} + + + +static void +isc_file_destroy_fn(IscHandlerList *hl, + IscSession *scb) +{ + if (scb->info.file.pathname) + { + isc_free(scb->info.file.pathname); + scb->info.file.pathname = NULL; + } +} + + + +/* +** Create a new session, insert it into a master structure +** and associate a filesystem object with it. +*/ +IscSession * +isc_openfile(IscMaster * mcb, + const char * pathname, + int openmode) +{ + IscSession * scb; + IscHandler * hcb; + int fd; + + + fd = open(pathname, openmode); + if (fd == -1) + return NULL; + + scb = isc_openfd(mcb, fd); + if (!scb) + return NULL; + + scb->type = ISC_TYPE_FILE; + scb->mode = openmode; + scb->info.file.pathname = isc_strdup(pathname); + + hcb = isc_newhandler(); + hcb->destroy = &isc_file_destroy_fn; + isc_pushhandler(scb, hcb); + + + return scb; +} + + + +int isc_disable(IscSession *scb) +{ + if (scb->state != ISC_STATE_RUNNING) + return -1; + + scb->state = ISC_STATE_DISABLED; + return 0; +} + + + +int isc_enable(IscSession *scb) +{ + if (scb->state != ISC_STATE_DISABLED) + return -1; + + scb->state = ISC_STATE_RUNNING; + return 0; +} + + diff --git a/src/libraries/libisc-new/src/isc_stdout.c b/src/libraries/libisc-new/src/isc_stdout.c new file mode 100644 index 0000000000000000000000000000000000000000..95f6aece23d4e699bd1acdd3e32117ca1ca189d7 --- /dev/null +++ b/src/libraries/libisc-new/src/isc_stdout.c @@ -0,0 +1,115 @@ +/* +** isc_stdout.c Some nice-to-have functions for output.. +** +** Copyright (c) 1991 Peter Eriksson and Per Cederqvist of the +** Lysator Academic Computer Association. +** +** history: +** 910305 pen moved into separate file +*/ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "isc.h" + + +extern int _printf( int (*fctn)(int chr), const char *format, ...); +extern void *memcpy(void *, const void *, int); + +int +isc_putc(int chr, + IscSession * scb) +{ + if (scb->state != ISC_STATE_CONNECTING && + scb->state != ISC_STATE_RUNNING) + return EOF; + + while (scb->sendindex == sizeof(scb->sendbuf)) + isc_flush(scb); + + return scb->sendbuf[scb->sendindex++] = chr; +} + + + +int +isc_write(IscSession * scb, + const void * buffer, + size_t length) +{ + const char * bp; + int len; + int blen; + int clen; + + + if (scb->state != ISC_STATE_CONNECTING && + scb->state != ISC_STATE_RUNNING) + return EOF; + + bp = (const char *) buffer; + clen = length; + + while (clen > 0) + { + blen = sizeof(scb->sendbuf) - scb->sendindex; + + /* Make room in sendbuf */ + while (blen == 0) + { + isc_flush(scb); + blen = sizeof(scb->sendbuf) - scb->sendindex; + } + + len = (clen > blen ? blen : clen); + + memcpy(scb->sendbuf+scb->sendindex, bp, len); + scb->sendindex += len; + clen -= len; + bp += len; + } + + return length; +} + + + +static IscSession * send_scb; /* Used by isc_printf */ + +static int send_putc(int c) +{ + return isc_putc(c, send_scb); +} + + +int +isc_vprintf(IscSession * scb, + const char * format, + va_list AP) +{ + send_scb = scb; + + return _printf(send_putc, format, AP); +} + + +int +isc_printf(IscSession * scb, + const char * format, + ...) +{ + va_list AP; + int cnt; + + + va_start(AP, format); + + send_scb = scb; + + cnt = _printf(send_putc, format, AP); + + va_end(AP); + + return cnt; +} diff --git a/src/libraries/libisc-new/src/printf.c b/src/libraries/libisc-new/src/printf.c new file mode 100644 index 0000000000000000000000000000000000000000..badac43883b1c49e98e5e7612a18fa678ecc28b1 --- /dev/null +++ b/src/libraries/libisc-new/src/printf.c @@ -0,0 +1,708 @@ +/* + * print.c + * + * + * THIS FILE IS A MODIFIED VERSION OF THE 'doprnt.c' FILE FROM THE BSD + * DISTRIBUTION. + * + * + * + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)doprnt.c 5.39 (Berkeley) 6/28/90"; +#endif /* LIBC_SCCS and not lint */ + +/* + * This is needed because of Suns include files. Sigh. + * (It dies in math.h) + */ +#if defined(__sparc__) && !defined(sparc) +#define sparc sparc +#endif +#if defined(__mc68020__) && !defined(mc68020) +#define mc68020 mc68020 +#endif +#if defined(__mc68010__) && !defined(mc68010) +#define mc68010 mc68010 +#endif +#if defined(__mc68000__) && !defined(mc68000) +#define mc68000 mc68000 +#endif + + +#include <sys/types.h> +#include <varargs.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <math.h> + + +/* + * External function declarations + */ +extern char *memchr(char *, int, int); + + +/* 11-bit exponent (VAX G floating point) is 308 decimal digits */ +#define MAXEXP 308 +/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ +#define MAXFRACT 39 + +#define DEFPREC 6 + +#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ + +#define PUTC(ch) (void) (*pr_chr)(ch) + +#define ARG(basetype) \ + _ulong = flags&LONGINT ? va_arg(argp, long basetype) : \ + flags&SHORTINT ? (short basetype)va_arg(argp, int) : \ + va_arg(argp, int) + +#define todigit(c) ((c) - '0') +#define tochar(n) ((n) + '0') + +/* Forward declarations */ +static int cvt(double, int, int, char *, u_char, char *, char *); +static char *round(double, int *, char *, char *, char, char *); +static char *exponent(char *, int, u_char); + + + +/* have to deal with the negative buffer count kludge */ +#define NEGATIVE_COUNT_KLUDGE + +#define LONGINT 0x01 /* long integer */ +#define LONGDBL 0x02 /* long double; unimplemented */ +#define SHORTINT 0x04 /* short integer */ +#define ALT 0x08 /* alternate form */ +#define LADJUST 0x10 /* left adjustment */ +#define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ +#define HEXPREFIX 0x40 /* add 0x or 0X prefix */ + +int _printf(pr_chr, fmt0, argp) + int (*pr_chr)(); + u_char *fmt0; + va_list argp; +{ + register u_char *fmt; /* format string */ +/* register int ch; character from fmt */ + register int cnt; /* return value accumulator */ + register int n; /* random handy integer */ + register char *t; /* buffer pointer */ + double _double; /* double precision arguments %[eEfgG] */ + u_long _ulong; /* integer arguments %[diouxX] */ + int base; /* base for [diouxX] conversion */ + int dprec; /* decimal precision in [diouxX] */ + int fieldsz; /* field size expanded by sign, etc */ + int flags; /* flags as above */ + int fpprec; /* `extra' floating precision in [eEfgG] */ + int prec; /* precision from format (%.3d), or -1 */ + int realsz; /* field size expanded by decimal precision */ + int size; /* size of converted field or string */ + int width; /* width from format (%8d), or 0 */ + char sign; /* sign prefix (' ', '+', '-', or \0) */ + char softsign; /* temporary negative sign for floats */ + char *digs; /* digits for [diouxX] conversion */ + char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ + + fmt = fmt0; + digs = "0123456789abcdef"; + for (cnt = 0;; ++fmt) + { + flags = 0; dprec = 0; fpprec = 0; width = 0; + prec = -1; + sign = '\0'; + + /* Print all non-format characters */ + while (*fmt && *fmt != '%' ) + { + cnt++; + (*pr_chr)(*fmt++); + } + + if (!*fmt) + return (cnt); + + +rflag: switch (*++fmt) + { + case ' ': + /* + * ``If the space and + flags both appear, the space + * flag will be ignored.'' + * -- ANSI X3J11 + */ + if (!sign) + sign = ' '; + goto rflag; + case '#': + flags |= ALT; + goto rflag; + case '*': + /* + * ``A negative field width argument is taken as a + * - flag followed by a positive field width.'' + * -- ANSI X3J11 + * They don't exclude field widths read from args. + */ + if ((width = va_arg(argp, int)) >= 0) + goto rflag; + width = -width; + /* FALLTHROUGH */ + case '-': + flags |= LADJUST; + goto rflag; + case '+': + sign = '+'; + goto rflag; + case '.': + if (*++fmt == '*') + n = va_arg(argp, int); + else { + n = 0; + while (isascii(*fmt) && isdigit(*fmt)) + n = 10 * n + todigit(*fmt++); + --fmt; + } + prec = n < 0 ? -1 : n; + goto rflag; + case '0': + /* + * ``Note that 0 is taken as a flag, not as the + * beginning of a field width.'' + * -- ANSI X3J11 + */ + flags |= ZEROPAD; + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + todigit(*fmt); + } while (isascii(*++fmt) && isdigit(*fmt)); + width = n; + --fmt; + goto rflag; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + flags |= SHORTINT; + goto rflag; + case 'l': + flags |= LONGINT; + goto rflag; + case 'c': + *(t = buf) = va_arg(argp, int); + size = 1; + sign = '\0'; + goto pforw; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + ARG(int); + if ((long)_ulong < 0) { + _ulong = -_ulong; + sign = '-'; + } + base = 10; + goto number; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + _double = va_arg(argp, double); + /* + * don't do unrealistic precision; just pad it with + * zeroes later, so buffer size stays rational. + */ + if (prec > MAXFRACT) { + if (*fmt != 'g' && *fmt != 'G' || (flags&ALT)) + fpprec = prec - MAXFRACT; + prec = MAXFRACT; + } + else if (prec == -1) + prec = DEFPREC; + /* + * softsign avoids negative 0 if _double is < 0 and + * no significant digits will be shown + */ + if (_double < 0) { + softsign = '-'; + _double = -_double; + } + else + softsign = 0; + /* + * cvt may have to round up past the "start" of the + * buffer, i.e. ``intf("%.2f", (double)9.999);''; + * if the first char isn't NULL, it did. + */ + *buf = '\0'; + size = cvt(_double, prec, flags, &softsign, *fmt, buf, + buf + sizeof(buf)); + if (softsign) + sign = '-'; + t = *buf ? buf : buf + 1; + goto pforw; + case 'n': + if (flags & LONGINT) + *va_arg(argp, long *) = cnt; + else if (flags & SHORTINT) + *va_arg(argp, short *) = cnt; + else + *va_arg(argp, int *) = cnt; + break; + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + ARG(unsigned); + base = 8; + goto nosign; + case 'p': + /* + * ``The argument shall be a pointer to void. The + * value of the pointer is converted to a sequence + * of printable characters, in an implementation- + * defined manner.'' + * -- ANSI X3J11 + */ + /* NOSTRICT */ + _ulong = (u_long)va_arg(argp, void *); + base = 16; + goto nosign; + case 's': + if (!(t = va_arg(argp, char *))) + t = "(null)"; + if (prec >= 0) { + /* + * can't use strlen; can only look for the + * NUL in the first `prec' characters, and + * strlen() will go further. + */ + char *p; + + if (p = memchr(t, 0, prec)) { + size = p - t; + if (size > prec) + size = prec; + } else + size = prec; + } else + size = strlen(t); + sign = '\0'; + goto pforw; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + ARG(unsigned); + base = 10; + goto nosign; + case 'X': + digs = "0123456789ABCDEF"; + /* FALLTHROUGH */ + case 'x': + ARG(unsigned); + base = 16; + /* leading 0x/X only if non-zero */ + if (flags & ALT && _ulong != 0) + flags |= HEXPREFIX; + + /* unsigned conversions */ +nosign: sign = '\0'; + /* + * ``... diouXx conversions ... if a precision is + * specified, the 0 flag will be ignored.'' + * -- ANSI X3J11 + */ +number: if ((dprec = prec) >= 0) + flags &= ~ZEROPAD; + + /* + * ``The result of converting a zero value with an + * explicit precision of zero is no characters.'' + * -- ANSI X3J11 + */ + t = buf + BUF; + if (_ulong != 0 || prec != 0) { + do { + *--t = digs[_ulong % base]; + _ulong /= base; + } while (_ulong); + digs = "0123456789abcdef"; + if (flags & ALT && base == 8 && *t != '0') + *--t = '0'; /* octal leading 0 */ + } + size = buf + BUF - t; + +pforw: + /* + * All reasonable formats wind up here. At this point, + * `t' points to a string which (if not flags&LADJUST) + * should be padded out to `width' places. If + * flags&ZEROPAD, it should first be prefixed by any + * sign or other prefix; otherwise, it should be blank + * padded before the prefix is emitted. After any + * left-hand padding and prefixing, emit zeroes + * required by a decimal [diouxX] precision, then print + * the string proper, then emit zeroes required by any + * leftover floating precision; finally, if LADJUST, + * pad with blanks. + */ + + /* + * compute actual size, so we know how much to pad + * fieldsz excludes decimal prec; realsz includes it + */ + fieldsz = size + fpprec; + if (sign) + fieldsz++; + if (flags & HEXPREFIX) + fieldsz += 2; + realsz = dprec > fieldsz ? dprec : fieldsz; + + /* right-adjusting blank padding */ + if ((flags & (LADJUST|ZEROPAD)) == 0 && width) + for (n = realsz; n < width; n++) + PUTC(' '); + /* prefix */ + if (sign) + PUTC(sign); + if (flags & HEXPREFIX) { + PUTC('0'); + PUTC((char)*fmt); + } + /* right-adjusting zero padding */ + if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) + for (n = realsz; n < width; n++) + PUTC('0'); + /* leading zeroes from decimal precision */ + for (n = fieldsz; n < dprec; n++) + PUTC('0'); + + /* the string or number proper */ + n = size; + while (--n >= 0) + PUTC(*t++); + /* trailing f.p. zeroes */ + while (--fpprec >= 0) + PUTC('0'); + /* left-adjusting padding (always blank) */ + if (flags & LADJUST) + for (n = realsz; n < width; n++) + PUTC(' '); + /* finally, adjust cnt */ + cnt += width > realsz ? width : realsz; + break; + case '\0': /* "%?" prints ?, unless ? is NULL */ + return (cnt); + default: + PUTC((char)*fmt); + cnt++; + } + } + /* NOTREACHED */ +} + +static int +cvt(double number, int prec, int flags, char *signp, + u_char fmtch, char *startp, char *endp) +{ + register char *p, *t; + register double fract; + int dotrim, expcnt, gformat; + double integer, tmp; + +#ifdef hp300 + if (expcnt = isspecial(number, startp, signp)) + return(expcnt); +#endif + + dotrim = expcnt = gformat = 0; + fract = modf(number, &integer); + + /* get an extra slot for rounding. */ + t = ++startp; + + /* + * get integer portion of number; put into the end of the buffer; the + * .01 is added for modf(356.0 / 10, &integer) returning .59999999... + */ + for (p = endp - 1; integer; ++expcnt) { + tmp = modf(integer / 10, &integer); + *p-- = tochar((int)((tmp + .01) * 10)); + } + switch(fmtch) { + case 'f': + /* reverse integer into beginning of buffer */ + if (expcnt) + for (; ++p < endp; *t++ = *p); + else + *t++ = '0'; + /* + * if precision required or alternate flag set, add in a + * decimal point. + */ + if (prec || flags&ALT) + *t++ = '.'; + /* if requires more precision and some fraction left */ + if (fract) { + if (prec) + do { + fract = modf(fract * 10, &tmp); + *t++ = tochar((int)tmp); + } while (--prec && fract); + if (fract) + startp = round(fract, (int *)NULL, startp, + t - 1, (char)0, signp); + } + for (; prec--; *t++ = '0'); + break; + case 'e': + case 'E': +eformat: if (expcnt) { + *t++ = *++p; + if (prec || flags&ALT) + *t++ = '.'; + /* if requires more precision and some integer left */ + for (; prec && ++p < endp; --prec) + *t++ = *p; + /* + * if done precision and more of the integer component, + * round using it; adjust fract so we don't re-round + * later. + */ + if (!prec && ++p < endp) { + fract = 0; + startp = round((double)0, &expcnt, startp, + t - 1, *p, signp); + } + /* adjust expcnt for digit in front of decimal */ + --expcnt; + } + /* until first fractional digit, decrement exponent */ + else if (fract) { + /* adjust expcnt for digit in front of decimal */ + for (expcnt = -1;; --expcnt) { + fract = modf(fract * 10, &tmp); + if (tmp) + break; + } + *t++ = tochar((int)tmp); + if (prec || flags&ALT) + *t++ = '.'; + } + else { + *t++ = '0'; + if (prec || flags&ALT) + *t++ = '.'; + } + /* if requires more precision and some fraction left */ + if (fract) { + if (prec) + do { + fract = modf(fract * 10, &tmp); + *t++ = tochar((int)tmp); + } while (--prec && fract); + if (fract) + startp = round(fract, &expcnt, startp, + t - 1, (char)0, signp); + } + /* if requires more precision */ + for (; prec--; *t++ = '0'); + + /* unless alternate flag, trim any g/G format trailing 0's */ + if (gformat && !(flags&ALT)) { + while (t > startp && *--t == '0'); + if (*t == '.') + --t; + ++t; + } + t = exponent(t, expcnt, fmtch); + break; + case 'g': + case 'G': + /* a precision of 0 is treated as a precision of 1. */ + if (!prec) + ++prec; + /* + * ``The style used depends on the value converted; style e + * will be used only if the exponent resulting from the + * conversion is less than -4 or greater than the precision.'' + * -- ANSI X3J11 + */ + if (expcnt > prec || !expcnt && fract && fract < .0001) { + /* + * g/G format counts "significant digits, not digits of + * precision; for the e/E format, this just causes an + * off-by-one problem, i.e. g/G considers the digit + * before the decimal point significant and e/E doesn't + * count it as precision. + */ + --prec; + fmtch -= 2; /* G->E, g->e */ + gformat = 1; + goto eformat; + } + /* + * reverse integer into beginning of buffer, + * note, decrement precision + */ + if (expcnt) + for (; ++p < endp; *t++ = *p, --prec); + else + *t++ = '0'; + /* + * if precision required or alternate flag set, add in a + * decimal point. If no digits yet, add in leading 0. + */ + if (prec || flags&ALT) { + dotrim = 1; + *t++ = '.'; + } + else + dotrim = 0; + /* if requires more precision and some fraction left */ + if (fract) { + if (prec) { + do { + fract = modf(fract * 10, &tmp); + *t++ = tochar((int)tmp); + } while(!tmp); + while (--prec && fract) { + fract = modf(fract * 10, &tmp); + *t++ = tochar((int)tmp); + } + } + if (fract) + startp = round(fract, (int *)NULL, startp, + t - 1, (char)0, signp); + } + /* alternate format, adds 0's for precision, else trim 0's */ + if (flags&ALT) + for (; prec--; *t++ = '0'); + else if (dotrim) { + while (t > startp && *--t == '0'); + if (*t != '.') + ++t; + } + } + return(t - startp); +} + +static char * +round(double fract, int *exp, char *start, char *end, char ch, char *signp) +{ + double tmp; + + if (fract) + (void)modf(fract * 10, &tmp); + else + tmp = todigit(ch); + if (tmp > 4) + for (;; --end) { + if (*end == '.') + --end; + if (++*end <= '9') + break; + *end = '0'; + if (end == start) { + if (exp) { /* e/E; increment exponent */ + *end = '1'; + ++*exp; + } + else { /* f; add extra digit */ + *--end = '1'; + --start; + } + break; + } + } + /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ + else if (*signp == '-') + for (;; --end) { + if (*end == '.') + --end; + if (*end != '0') + break; + if (end == start) + *signp = 0; + } + return(start); +} + +static char * +exponent(char *p, int exp, u_char fmtch) +{ + register char *t; + char expbuf[MAXEXP]; + + *p++ = fmtch; + if (exp < 0) { + exp = -exp; + *p++ = '-'; + } + else + *p++ = '+'; + t = expbuf + MAXEXP; + if (exp > 9) { + do { + *--t = tochar(exp % 10); + } while ((exp /= 10) > 9); + *--t = tochar(exp); + for (; t < expbuf + MAXEXP; *p++ = *t++); + } + else { + *p++ = '0'; + *p++ = tochar(exp); + } + return(p); +} + +#ifdef hp300 +isspecial(d, bufp, signp) + double d; + char *bufp, *signp; +{ + register struct IEEEdp { + unsigned sign:1; + unsigned exp:11; + unsigned manh:20; + unsigned manl:32; + } *ip = (struct IEEEdp *)&d; + + if (ip->exp != 0x7ff) + return(0); + if (ip->manh || ip->manl) + (void)strcpy(bufp, "NaN"); + else + (void)strcpy(bufp, "Inf"); + return(3); +} +#endif +