1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * All rights reserved. 5*7c478bd9Sstevel@tonic-gate * 6*7c478bd9Sstevel@tonic-gate * Permission is hereby granted, free of charge, to any person obtaining a 7*7c478bd9Sstevel@tonic-gate * copy of this software and associated documentation files (the 8*7c478bd9Sstevel@tonic-gate * "Software"), to deal in the Software without restriction, including 9*7c478bd9Sstevel@tonic-gate * without limitation the rights to use, copy, modify, merge, publish, 10*7c478bd9Sstevel@tonic-gate * distribute, and/or sell copies of the Software, and to permit persons 11*7c478bd9Sstevel@tonic-gate * to whom the Software is furnished to do so, provided that the above 12*7c478bd9Sstevel@tonic-gate * copyright notice(s) and this permission notice appear in all copies of 13*7c478bd9Sstevel@tonic-gate * the Software and that both the above copyright notice(s) and this 14*7c478bd9Sstevel@tonic-gate * permission notice appear in supporting documentation. 15*7c478bd9Sstevel@tonic-gate * 16*7c478bd9Sstevel@tonic-gate * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17*7c478bd9Sstevel@tonic-gate * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18*7c478bd9Sstevel@tonic-gate * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 19*7c478bd9Sstevel@tonic-gate * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 20*7c478bd9Sstevel@tonic-gate * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 21*7c478bd9Sstevel@tonic-gate * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING 22*7c478bd9Sstevel@tonic-gate * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 23*7c478bd9Sstevel@tonic-gate * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 24*7c478bd9Sstevel@tonic-gate * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 25*7c478bd9Sstevel@tonic-gate * 26*7c478bd9Sstevel@tonic-gate * Except as contained in this notice, the name of a copyright holder 27*7c478bd9Sstevel@tonic-gate * shall not be used in advertising or otherwise to promote the sale, use 28*7c478bd9Sstevel@tonic-gate * or other dealings in this Software without prior written authorization 29*7c478bd9Sstevel@tonic-gate * of the copyright holder. 30*7c478bd9Sstevel@tonic-gate */ 31*7c478bd9Sstevel@tonic-gate 32*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 33*7c478bd9Sstevel@tonic-gate 34*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 35*7c478bd9Sstevel@tonic-gate #include <stdio.h> 36*7c478bd9Sstevel@tonic-gate #include <string.h> 37*7c478bd9Sstevel@tonic-gate #include <errno.h> 38*7c478bd9Sstevel@tonic-gate 39*7c478bd9Sstevel@tonic-gate #include "ioutil.h" 40*7c478bd9Sstevel@tonic-gate #include "chrqueue.h" 41*7c478bd9Sstevel@tonic-gate #include "freelist.h" 42*7c478bd9Sstevel@tonic-gate #include "errmsg.h" 43*7c478bd9Sstevel@tonic-gate 44*7c478bd9Sstevel@tonic-gate /* 45*7c478bd9Sstevel@tonic-gate * Set the number of bytes allocated to each node of the list of 46*7c478bd9Sstevel@tonic-gate * character buffers. This facility is designed principally as 47*7c478bd9Sstevel@tonic-gate * an expandible I/O output buffer, so use the stdio buffer size 48*7c478bd9Sstevel@tonic-gate * where available. 49*7c478bd9Sstevel@tonic-gate */ 50*7c478bd9Sstevel@tonic-gate #ifdef BUFSIZ 51*7c478bd9Sstevel@tonic-gate #define GL_CQ_SIZE BUFSIZ 52*7c478bd9Sstevel@tonic-gate #else 53*7c478bd9Sstevel@tonic-gate #define GL_CQ_SIZE 512 54*7c478bd9Sstevel@tonic-gate #endif 55*7c478bd9Sstevel@tonic-gate 56*7c478bd9Sstevel@tonic-gate /* 57*7c478bd9Sstevel@tonic-gate * The queue is contained in a list of fixed sized buffers. New nodes 58*7c478bd9Sstevel@tonic-gate * are appended to this list as needed to accomodate newly added bytes. 59*7c478bd9Sstevel@tonic-gate * Old nodes at the head of the list are removed as they are emptied. 60*7c478bd9Sstevel@tonic-gate */ 61*7c478bd9Sstevel@tonic-gate typedef struct CqCharBuff CqCharBuff; 62*7c478bd9Sstevel@tonic-gate struct CqCharBuff { 63*7c478bd9Sstevel@tonic-gate CqCharBuff *next; /* The next node in the list of buffers */ 64*7c478bd9Sstevel@tonic-gate char bytes[GL_CQ_SIZE]; /* The fixed size buffer of this node */ 65*7c478bd9Sstevel@tonic-gate }; 66*7c478bd9Sstevel@tonic-gate 67*7c478bd9Sstevel@tonic-gate /* 68*7c478bd9Sstevel@tonic-gate * Define the structure that is used to contain a list of character 69*7c478bd9Sstevel@tonic-gate * buffers. 70*7c478bd9Sstevel@tonic-gate */ 71*7c478bd9Sstevel@tonic-gate struct GlCharQueue { 72*7c478bd9Sstevel@tonic-gate ErrMsg *err; /* A buffer in which to record error messages */ 73*7c478bd9Sstevel@tonic-gate FreeList *bufmem; /* A free-list of CqCharBuff structures */ 74*7c478bd9Sstevel@tonic-gate struct { 75*7c478bd9Sstevel@tonic-gate CqCharBuff *head; /* The head of the list of output buffers */ 76*7c478bd9Sstevel@tonic-gate CqCharBuff *tail; /* The tail of the list of output buffers */ 77*7c478bd9Sstevel@tonic-gate } buffers; 78*7c478bd9Sstevel@tonic-gate int nflush; /* The total number of characters that have been */ 79*7c478bd9Sstevel@tonic-gate /* flushed from the start of the queue since */ 80*7c478bd9Sstevel@tonic-gate /* _glq_empty_queue() was last called. */ 81*7c478bd9Sstevel@tonic-gate int ntotal; /* The total number of characters that have been */ 82*7c478bd9Sstevel@tonic-gate /* appended to the queue since _glq_empty_queue() */ 83*7c478bd9Sstevel@tonic-gate /* was last called. */ 84*7c478bd9Sstevel@tonic-gate }; 85*7c478bd9Sstevel@tonic-gate 86*7c478bd9Sstevel@tonic-gate /*....................................................................... 87*7c478bd9Sstevel@tonic-gate * Create a new GlCharQueue object. 88*7c478bd9Sstevel@tonic-gate * 89*7c478bd9Sstevel@tonic-gate * Output: 90*7c478bd9Sstevel@tonic-gate * return GlCharQueue * The new object, or NULL on error. 91*7c478bd9Sstevel@tonic-gate */ 92*7c478bd9Sstevel@tonic-gate GlCharQueue *_new_GlCharQueue(void) 93*7c478bd9Sstevel@tonic-gate { 94*7c478bd9Sstevel@tonic-gate GlCharQueue *cq; /* The object to be returned */ 95*7c478bd9Sstevel@tonic-gate /* 96*7c478bd9Sstevel@tonic-gate * Allocate the container. 97*7c478bd9Sstevel@tonic-gate */ 98*7c478bd9Sstevel@tonic-gate cq = malloc(sizeof(GlCharQueue)); 99*7c478bd9Sstevel@tonic-gate if(!cq) { 100*7c478bd9Sstevel@tonic-gate errno = ENOMEM; 101*7c478bd9Sstevel@tonic-gate return NULL; 102*7c478bd9Sstevel@tonic-gate }; 103*7c478bd9Sstevel@tonic-gate /* 104*7c478bd9Sstevel@tonic-gate * Before attempting any operation that might fail, initialize the 105*7c478bd9Sstevel@tonic-gate * container at least up to the point at which it can safely be passed 106*7c478bd9Sstevel@tonic-gate * to del_GlCharQueue(). 107*7c478bd9Sstevel@tonic-gate */ 108*7c478bd9Sstevel@tonic-gate cq->err = NULL; 109*7c478bd9Sstevel@tonic-gate cq->bufmem = NULL; 110*7c478bd9Sstevel@tonic-gate cq->buffers.head = NULL; 111*7c478bd9Sstevel@tonic-gate cq->buffers.tail = NULL; 112*7c478bd9Sstevel@tonic-gate cq->nflush = cq->ntotal = 0; 113*7c478bd9Sstevel@tonic-gate /* 114*7c478bd9Sstevel@tonic-gate * Allocate a place to record error messages. 115*7c478bd9Sstevel@tonic-gate */ 116*7c478bd9Sstevel@tonic-gate cq->err = _new_ErrMsg(); 117*7c478bd9Sstevel@tonic-gate if(!cq->err) 118*7c478bd9Sstevel@tonic-gate return _del_GlCharQueue(cq); 119*7c478bd9Sstevel@tonic-gate /* 120*7c478bd9Sstevel@tonic-gate * Allocate the freelist of CqCharBuff structures. 121*7c478bd9Sstevel@tonic-gate */ 122*7c478bd9Sstevel@tonic-gate cq->bufmem = _new_FreeList(sizeof(CqCharBuff), 1); 123*7c478bd9Sstevel@tonic-gate if(!cq->bufmem) 124*7c478bd9Sstevel@tonic-gate return _del_GlCharQueue(cq); 125*7c478bd9Sstevel@tonic-gate return cq; 126*7c478bd9Sstevel@tonic-gate } 127*7c478bd9Sstevel@tonic-gate 128*7c478bd9Sstevel@tonic-gate /*....................................................................... 129*7c478bd9Sstevel@tonic-gate * Delete a GlCharQueue object. 130*7c478bd9Sstevel@tonic-gate * 131*7c478bd9Sstevel@tonic-gate * Input: 132*7c478bd9Sstevel@tonic-gate * cq GlCharQueue * The object to be deleted. 133*7c478bd9Sstevel@tonic-gate * Output: 134*7c478bd9Sstevel@tonic-gate * return GlCharQueue * The deleted object (always NULL). 135*7c478bd9Sstevel@tonic-gate */ 136*7c478bd9Sstevel@tonic-gate GlCharQueue *_del_GlCharQueue(GlCharQueue *cq) 137*7c478bd9Sstevel@tonic-gate { 138*7c478bd9Sstevel@tonic-gate if(cq) { 139*7c478bd9Sstevel@tonic-gate cq->err = _del_ErrMsg(cq->err); 140*7c478bd9Sstevel@tonic-gate cq->bufmem = _del_FreeList(cq->bufmem, 1); 141*7c478bd9Sstevel@tonic-gate free(cq); 142*7c478bd9Sstevel@tonic-gate }; 143*7c478bd9Sstevel@tonic-gate return NULL; 144*7c478bd9Sstevel@tonic-gate } 145*7c478bd9Sstevel@tonic-gate 146*7c478bd9Sstevel@tonic-gate /*....................................................................... 147*7c478bd9Sstevel@tonic-gate * Append an array of n characters to a character queue. 148*7c478bd9Sstevel@tonic-gate * 149*7c478bd9Sstevel@tonic-gate * Input: 150*7c478bd9Sstevel@tonic-gate * cq GlCharQueue * The queue to append to. 151*7c478bd9Sstevel@tonic-gate * chars const char * The array of n characters to be appended. 152*7c478bd9Sstevel@tonic-gate * n int The number of characters in chars[]. 153*7c478bd9Sstevel@tonic-gate * write_fn GL_WRITE_FN * The function to call to output characters, 154*7c478bd9Sstevel@tonic-gate * or 0 to simply discard the contents of the 155*7c478bd9Sstevel@tonic-gate * queue. This will be called whenever the 156*7c478bd9Sstevel@tonic-gate * buffer becomes full. If it fails to release 157*7c478bd9Sstevel@tonic-gate * any space, the buffer will be extended. 158*7c478bd9Sstevel@tonic-gate * data void * Anonymous data to pass to write_fn(). 159*7c478bd9Sstevel@tonic-gate * Output: 160*7c478bd9Sstevel@tonic-gate * return int The number of characters successfully 161*7c478bd9Sstevel@tonic-gate * appended. This will only be < n on error. 162*7c478bd9Sstevel@tonic-gate */ 163*7c478bd9Sstevel@tonic-gate int _glq_append_chars(GlCharQueue *cq, const char *chars, int n, 164*7c478bd9Sstevel@tonic-gate GlWriteFn *write_fn, void *data) 165*7c478bd9Sstevel@tonic-gate { 166*7c478bd9Sstevel@tonic-gate int ndone = 0; /* The number of characters appended so far */ 167*7c478bd9Sstevel@tonic-gate /* 168*7c478bd9Sstevel@tonic-gate * Check the arguments. 169*7c478bd9Sstevel@tonic-gate */ 170*7c478bd9Sstevel@tonic-gate if(!cq || !chars) { 171*7c478bd9Sstevel@tonic-gate errno = EINVAL; 172*7c478bd9Sstevel@tonic-gate return 0; 173*7c478bd9Sstevel@tonic-gate }; 174*7c478bd9Sstevel@tonic-gate /* 175*7c478bd9Sstevel@tonic-gate * The appended characters may have to be split between multiple 176*7c478bd9Sstevel@tonic-gate * buffers, so loop for each buffer. 177*7c478bd9Sstevel@tonic-gate */ 178*7c478bd9Sstevel@tonic-gate while(ndone < n) { 179*7c478bd9Sstevel@tonic-gate int ntodo; /* The number of characters remaining to be appended */ 180*7c478bd9Sstevel@tonic-gate int nleft; /* The amount of space remaining in cq->buffers.tail */ 181*7c478bd9Sstevel@tonic-gate int nnew; /* The number of characters to append to cq->buffers.tail */ 182*7c478bd9Sstevel@tonic-gate /* 183*7c478bd9Sstevel@tonic-gate * Compute the offset at which the next character should be written 184*7c478bd9Sstevel@tonic-gate * into the tail buffer segment. 185*7c478bd9Sstevel@tonic-gate */ 186*7c478bd9Sstevel@tonic-gate int boff = cq->ntotal % GL_CQ_SIZE; 187*7c478bd9Sstevel@tonic-gate /* 188*7c478bd9Sstevel@tonic-gate * Since we don't allocate a new buffer until we have at least one 189*7c478bd9Sstevel@tonic-gate * character to write into it, if boff is 0 at this point, it means 190*7c478bd9Sstevel@tonic-gate * that we hit the end of the tail buffer segment on the last append, 191*7c478bd9Sstevel@tonic-gate * so we need to allocate a new one. 192*7c478bd9Sstevel@tonic-gate * 193*7c478bd9Sstevel@tonic-gate * If allocating this new node will require a call to malloc(), as 194*7c478bd9Sstevel@tonic-gate * opposed to using a currently unused node in the freelist, first try 195*7c478bd9Sstevel@tonic-gate * flushing the current contents of the buffer to the terminal. When 196*7c478bd9Sstevel@tonic-gate * write_fn() uses blocking I/O, this stops the buffer size ever getting 197*7c478bd9Sstevel@tonic-gate * bigger than a single buffer node. When it is non-blocking, it helps 198*7c478bd9Sstevel@tonic-gate * to keep the amount of memory, but it isn't gauranteed to do so. 199*7c478bd9Sstevel@tonic-gate */ 200*7c478bd9Sstevel@tonic-gate if(boff == 0 && _idle_FreeListNodes(cq->bufmem) == 0) { 201*7c478bd9Sstevel@tonic-gate switch(_glq_flush_queue(cq, write_fn, data)) { 202*7c478bd9Sstevel@tonic-gate case GLQ_FLUSH_DONE: 203*7c478bd9Sstevel@tonic-gate break; 204*7c478bd9Sstevel@tonic-gate case GLQ_FLUSH_AGAIN: 205*7c478bd9Sstevel@tonic-gate errno = 0; /* Don't confuse the caller */ 206*7c478bd9Sstevel@tonic-gate break; 207*7c478bd9Sstevel@tonic-gate default: 208*7c478bd9Sstevel@tonic-gate return ndone; /* Error */ 209*7c478bd9Sstevel@tonic-gate }; 210*7c478bd9Sstevel@tonic-gate boff = cq->ntotal % GL_CQ_SIZE; 211*7c478bd9Sstevel@tonic-gate }; 212*7c478bd9Sstevel@tonic-gate /* 213*7c478bd9Sstevel@tonic-gate * Since we don't allocate a new buffer until we have at least one 214*7c478bd9Sstevel@tonic-gate * character to write into it, if boff is 0 at this point, it means 215*7c478bd9Sstevel@tonic-gate * that we hit the end of the tail buffer segment on the last append, 216*7c478bd9Sstevel@tonic-gate * so we need to allocate a new one. 217*7c478bd9Sstevel@tonic-gate */ 218*7c478bd9Sstevel@tonic-gate if(boff == 0) { 219*7c478bd9Sstevel@tonic-gate /* 220*7c478bd9Sstevel@tonic-gate * Allocate the new node. 221*7c478bd9Sstevel@tonic-gate */ 222*7c478bd9Sstevel@tonic-gate CqCharBuff *node = (CqCharBuff *) _new_FreeListNode(cq->bufmem); 223*7c478bd9Sstevel@tonic-gate if(!node) { 224*7c478bd9Sstevel@tonic-gate _err_record_msg(cq->err, "Insufficient memory to buffer output.", 225*7c478bd9Sstevel@tonic-gate END_ERR_MSG); 226*7c478bd9Sstevel@tonic-gate return ndone; 227*7c478bd9Sstevel@tonic-gate }; 228*7c478bd9Sstevel@tonic-gate /* 229*7c478bd9Sstevel@tonic-gate * Initialize the node. 230*7c478bd9Sstevel@tonic-gate */ 231*7c478bd9Sstevel@tonic-gate node->next = NULL; 232*7c478bd9Sstevel@tonic-gate /* 233*7c478bd9Sstevel@tonic-gate * Append the new node to the tail of the list. 234*7c478bd9Sstevel@tonic-gate */ 235*7c478bd9Sstevel@tonic-gate if(cq->buffers.tail) 236*7c478bd9Sstevel@tonic-gate cq->buffers.tail->next = node; 237*7c478bd9Sstevel@tonic-gate else 238*7c478bd9Sstevel@tonic-gate cq->buffers.head = node; 239*7c478bd9Sstevel@tonic-gate cq->buffers.tail = node; 240*7c478bd9Sstevel@tonic-gate }; 241*7c478bd9Sstevel@tonic-gate /* 242*7c478bd9Sstevel@tonic-gate * How much room is there for new characters in the current tail node? 243*7c478bd9Sstevel@tonic-gate */ 244*7c478bd9Sstevel@tonic-gate nleft = GL_CQ_SIZE - boff; 245*7c478bd9Sstevel@tonic-gate /* 246*7c478bd9Sstevel@tonic-gate * How many characters remain to be appended? 247*7c478bd9Sstevel@tonic-gate */ 248*7c478bd9Sstevel@tonic-gate ntodo = n - ndone; 249*7c478bd9Sstevel@tonic-gate /* 250*7c478bd9Sstevel@tonic-gate * How many characters should we append to the current tail node? 251*7c478bd9Sstevel@tonic-gate */ 252*7c478bd9Sstevel@tonic-gate nnew = nleft < ntodo ? nleft : ntodo; 253*7c478bd9Sstevel@tonic-gate /* 254*7c478bd9Sstevel@tonic-gate * Append the latest prefix of nnew characters. 255*7c478bd9Sstevel@tonic-gate */ 256*7c478bd9Sstevel@tonic-gate memcpy(cq->buffers.tail->bytes + boff, chars + ndone, nnew); 257*7c478bd9Sstevel@tonic-gate cq->ntotal += nnew; 258*7c478bd9Sstevel@tonic-gate ndone += nnew; 259*7c478bd9Sstevel@tonic-gate }; 260*7c478bd9Sstevel@tonic-gate /* 261*7c478bd9Sstevel@tonic-gate * Return the count of the number of characters successfully appended. 262*7c478bd9Sstevel@tonic-gate */ 263*7c478bd9Sstevel@tonic-gate return ndone; 264*7c478bd9Sstevel@tonic-gate } 265*7c478bd9Sstevel@tonic-gate 266*7c478bd9Sstevel@tonic-gate /*....................................................................... 267*7c478bd9Sstevel@tonic-gate * Discard the contents of a queue of characters. 268*7c478bd9Sstevel@tonic-gate * 269*7c478bd9Sstevel@tonic-gate * Input: 270*7c478bd9Sstevel@tonic-gate * cq GlCharQueue * The queue to clear. 271*7c478bd9Sstevel@tonic-gate */ 272*7c478bd9Sstevel@tonic-gate void _glq_empty_queue(GlCharQueue *cq) 273*7c478bd9Sstevel@tonic-gate { 274*7c478bd9Sstevel@tonic-gate if(cq) { 275*7c478bd9Sstevel@tonic-gate /* 276*7c478bd9Sstevel@tonic-gate * Return all list nodes to their respective free-lists. 277*7c478bd9Sstevel@tonic-gate */ 278*7c478bd9Sstevel@tonic-gate _rst_FreeList(cq->bufmem); 279*7c478bd9Sstevel@tonic-gate /* 280*7c478bd9Sstevel@tonic-gate * Mark the lists as empty. 281*7c478bd9Sstevel@tonic-gate */ 282*7c478bd9Sstevel@tonic-gate cq->buffers.head = cq->buffers.tail = NULL; 283*7c478bd9Sstevel@tonic-gate cq->nflush = cq->ntotal = 0; 284*7c478bd9Sstevel@tonic-gate }; 285*7c478bd9Sstevel@tonic-gate } 286*7c478bd9Sstevel@tonic-gate 287*7c478bd9Sstevel@tonic-gate /*....................................................................... 288*7c478bd9Sstevel@tonic-gate * Return a count of the number of characters currently in the queue. 289*7c478bd9Sstevel@tonic-gate * 290*7c478bd9Sstevel@tonic-gate * Input: 291*7c478bd9Sstevel@tonic-gate * cq GlCharQueue * The queue of interest. 292*7c478bd9Sstevel@tonic-gate * Output: 293*7c478bd9Sstevel@tonic-gate * return int The number of characters in the queue. 294*7c478bd9Sstevel@tonic-gate */ 295*7c478bd9Sstevel@tonic-gate int _glq_char_count(GlCharQueue *cq) 296*7c478bd9Sstevel@tonic-gate { 297*7c478bd9Sstevel@tonic-gate return (cq && cq->buffers.head) ? (cq->ntotal - cq->nflush) : 0; 298*7c478bd9Sstevel@tonic-gate } 299*7c478bd9Sstevel@tonic-gate 300*7c478bd9Sstevel@tonic-gate /*....................................................................... 301*7c478bd9Sstevel@tonic-gate * Write as many characters as possible from the start of a character 302*7c478bd9Sstevel@tonic-gate * queue via a given output callback function, removing those written 303*7c478bd9Sstevel@tonic-gate * from the queue. 304*7c478bd9Sstevel@tonic-gate * 305*7c478bd9Sstevel@tonic-gate * Input: 306*7c478bd9Sstevel@tonic-gate * cq GlCharQueue * The queue to write characters from. 307*7c478bd9Sstevel@tonic-gate * write_fn GL_WRITE_FN * The function to call to output characters, 308*7c478bd9Sstevel@tonic-gate * or 0 to simply discard the contents of the 309*7c478bd9Sstevel@tonic-gate * queue. 310*7c478bd9Sstevel@tonic-gate * data void * Anonymous data to pass to write_fn(). 311*7c478bd9Sstevel@tonic-gate * Output: 312*7c478bd9Sstevel@tonic-gate * return GlFlushState The status of the flush operation: 313*7c478bd9Sstevel@tonic-gate * GLQ_FLUSH_DONE - The flush operation 314*7c478bd9Sstevel@tonic-gate * completed successfully. 315*7c478bd9Sstevel@tonic-gate * GLQ_FLUSH_AGAIN - The flush operation 316*7c478bd9Sstevel@tonic-gate * couldn't be completed 317*7c478bd9Sstevel@tonic-gate * on this call. Call this 318*7c478bd9Sstevel@tonic-gate * function again when the 319*7c478bd9Sstevel@tonic-gate * output channel can accept 320*7c478bd9Sstevel@tonic-gate * further output. 321*7c478bd9Sstevel@tonic-gate * GLQ_FLUSH_ERROR Unrecoverable error. 322*7c478bd9Sstevel@tonic-gate */ 323*7c478bd9Sstevel@tonic-gate GlqFlushState _glq_flush_queue(GlCharQueue *cq, GlWriteFn *write_fn, 324*7c478bd9Sstevel@tonic-gate void *data) 325*7c478bd9Sstevel@tonic-gate { 326*7c478bd9Sstevel@tonic-gate /* 327*7c478bd9Sstevel@tonic-gate * Check the arguments. 328*7c478bd9Sstevel@tonic-gate */ 329*7c478bd9Sstevel@tonic-gate if(!cq) { 330*7c478bd9Sstevel@tonic-gate errno = EINVAL; 331*7c478bd9Sstevel@tonic-gate return GLQ_FLUSH_ERROR; 332*7c478bd9Sstevel@tonic-gate }; 333*7c478bd9Sstevel@tonic-gate /* 334*7c478bd9Sstevel@tonic-gate * If possible keep writing until all of the chained buffers have been 335*7c478bd9Sstevel@tonic-gate * emptied and removed from the list. 336*7c478bd9Sstevel@tonic-gate */ 337*7c478bd9Sstevel@tonic-gate while(cq->buffers.head) { 338*7c478bd9Sstevel@tonic-gate /* 339*7c478bd9Sstevel@tonic-gate * Are we looking at the only node in the list? 340*7c478bd9Sstevel@tonic-gate */ 341*7c478bd9Sstevel@tonic-gate int is_tail = cq->buffers.head == cq->buffers.tail; 342*7c478bd9Sstevel@tonic-gate /* 343*7c478bd9Sstevel@tonic-gate * How many characters more than an exact multiple of the buffer-segment 344*7c478bd9Sstevel@tonic-gate * size have been added to the buffer so far? 345*7c478bd9Sstevel@tonic-gate */ 346*7c478bd9Sstevel@tonic-gate int nmodulo = cq->ntotal % GL_CQ_SIZE; 347*7c478bd9Sstevel@tonic-gate /* 348*7c478bd9Sstevel@tonic-gate * How many characters of the buffer segment at the head of the list 349*7c478bd9Sstevel@tonic-gate * have been used? Note that this includes any characters that have 350*7c478bd9Sstevel@tonic-gate * already been flushed. Also note that if nmodulo==0, this means that 351*7c478bd9Sstevel@tonic-gate * the tail buffer segment is full. The reason for this is that we 352*7c478bd9Sstevel@tonic-gate * don't allocate new tail buffer segments until there is at least one 353*7c478bd9Sstevel@tonic-gate * character to be added to them. 354*7c478bd9Sstevel@tonic-gate */ 355*7c478bd9Sstevel@tonic-gate int nhead = (!is_tail || nmodulo == 0) ? GL_CQ_SIZE : nmodulo; 356*7c478bd9Sstevel@tonic-gate /* 357*7c478bd9Sstevel@tonic-gate * How many characters remain to be flushed from the buffer 358*7c478bd9Sstevel@tonic-gate * at the head of the list? 359*7c478bd9Sstevel@tonic-gate */ 360*7c478bd9Sstevel@tonic-gate int nbuff = nhead - (cq->nflush % GL_CQ_SIZE); 361*7c478bd9Sstevel@tonic-gate /* 362*7c478bd9Sstevel@tonic-gate * Attempt to write this number. 363*7c478bd9Sstevel@tonic-gate */ 364*7c478bd9Sstevel@tonic-gate int nnew = write_fn(data, cq->buffers.head->bytes + 365*7c478bd9Sstevel@tonic-gate cq->nflush % GL_CQ_SIZE, nbuff); 366*7c478bd9Sstevel@tonic-gate /* 367*7c478bd9Sstevel@tonic-gate * Was anything written? 368*7c478bd9Sstevel@tonic-gate */ 369*7c478bd9Sstevel@tonic-gate if(nnew > 0) { 370*7c478bd9Sstevel@tonic-gate /* 371*7c478bd9Sstevel@tonic-gate * Increment the count of the number of characters that have 372*7c478bd9Sstevel@tonic-gate * been flushed from the head of the queue. 373*7c478bd9Sstevel@tonic-gate */ 374*7c478bd9Sstevel@tonic-gate cq->nflush += nnew; 375*7c478bd9Sstevel@tonic-gate /* 376*7c478bd9Sstevel@tonic-gate * If we succeded in writing all of the contents of the current 377*7c478bd9Sstevel@tonic-gate * buffer segment, remove it from the queue. 378*7c478bd9Sstevel@tonic-gate */ 379*7c478bd9Sstevel@tonic-gate if(nnew == nbuff) { 380*7c478bd9Sstevel@tonic-gate /* 381*7c478bd9Sstevel@tonic-gate * If we just emptied the last node left in the list, then the queue is 382*7c478bd9Sstevel@tonic-gate * now empty and should be reset. 383*7c478bd9Sstevel@tonic-gate */ 384*7c478bd9Sstevel@tonic-gate if(is_tail) { 385*7c478bd9Sstevel@tonic-gate _glq_empty_queue(cq); 386*7c478bd9Sstevel@tonic-gate } else { 387*7c478bd9Sstevel@tonic-gate /* 388*7c478bd9Sstevel@tonic-gate * Get the node to be removed from the head of the list. 389*7c478bd9Sstevel@tonic-gate */ 390*7c478bd9Sstevel@tonic-gate CqCharBuff *node = cq->buffers.head; 391*7c478bd9Sstevel@tonic-gate /* 392*7c478bd9Sstevel@tonic-gate * Make the node that follows it the new head of the queue. 393*7c478bd9Sstevel@tonic-gate */ 394*7c478bd9Sstevel@tonic-gate cq->buffers.head = node->next; 395*7c478bd9Sstevel@tonic-gate /* 396*7c478bd9Sstevel@tonic-gate * Return it to the freelist. 397*7c478bd9Sstevel@tonic-gate */ 398*7c478bd9Sstevel@tonic-gate node = (CqCharBuff *) _del_FreeListNode(cq->bufmem, node); 399*7c478bd9Sstevel@tonic-gate }; 400*7c478bd9Sstevel@tonic-gate }; 401*7c478bd9Sstevel@tonic-gate /* 402*7c478bd9Sstevel@tonic-gate * If the write blocked, request that this function be called again 403*7c478bd9Sstevel@tonic-gate * when space to write next becomes available. 404*7c478bd9Sstevel@tonic-gate */ 405*7c478bd9Sstevel@tonic-gate } else if(nnew==0) { 406*7c478bd9Sstevel@tonic-gate return GLQ_FLUSH_AGAIN; 407*7c478bd9Sstevel@tonic-gate /* 408*7c478bd9Sstevel@tonic-gate * I/O error. 409*7c478bd9Sstevel@tonic-gate */ 410*7c478bd9Sstevel@tonic-gate } else { 411*7c478bd9Sstevel@tonic-gate _err_record_msg(cq->err, "Error writing to terminal", END_ERR_MSG); 412*7c478bd9Sstevel@tonic-gate return GLQ_FLUSH_ERROR; 413*7c478bd9Sstevel@tonic-gate }; 414*7c478bd9Sstevel@tonic-gate }; 415*7c478bd9Sstevel@tonic-gate /* 416*7c478bd9Sstevel@tonic-gate * To get here the queue must now be empty. 417*7c478bd9Sstevel@tonic-gate */ 418*7c478bd9Sstevel@tonic-gate return GLQ_FLUSH_DONE; 419*7c478bd9Sstevel@tonic-gate } 420*7c478bd9Sstevel@tonic-gate 421*7c478bd9Sstevel@tonic-gate /*....................................................................... 422*7c478bd9Sstevel@tonic-gate * Return extra information (ie. in addition to that provided by errno) 423*7c478bd9Sstevel@tonic-gate * about the last error to occur in any of the public functions of this 424*7c478bd9Sstevel@tonic-gate * module. 425*7c478bd9Sstevel@tonic-gate * 426*7c478bd9Sstevel@tonic-gate * Input: 427*7c478bd9Sstevel@tonic-gate * cq GlCharQueue * The container of the history list. 428*7c478bd9Sstevel@tonic-gate * Output: 429*7c478bd9Sstevel@tonic-gate * return const char * A pointer to the internal buffer in which 430*7c478bd9Sstevel@tonic-gate * the error message is temporarily stored. 431*7c478bd9Sstevel@tonic-gate */ 432*7c478bd9Sstevel@tonic-gate const char *_glq_last_error(GlCharQueue *cq) 433*7c478bd9Sstevel@tonic-gate { 434*7c478bd9Sstevel@tonic-gate return cq ? _err_get_msg(cq->err) : "NULL GlCharQueue argument"; 435*7c478bd9Sstevel@tonic-gate } 436