2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, and/or sell copies of the Software, and to permit persons
11 * to whom the Software is furnished to do so, provided that the above
12 * copyright notice(s) and this permission notice appear in all copies of
13 * the Software and that both the above copyright notice(s) and this
14 * permission notice appear in supporting documentation.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 * Except as contained in this notice, the name of a copyright holder
27 * shall not be used in advertising or otherwise to promote the sale, use
28 * or other dealings in this Software without prior written authorization
29 * of the copyright holder.
32 #pragma ident "%Z%%M% %I% %E% SMI"
45 * Set the number of bytes allocated to each node of the list of
46 * character buffers. This facility is designed principally as
47 * an expandible I/O output buffer, so use the stdio buffer size
51 #define GL_CQ_SIZE BUFSIZ
53 #define GL_CQ_SIZE 512
57 * The queue is contained in a list of fixed sized buffers. New nodes
58 * are appended to this list as needed to accomodate newly added bytes.
59 * Old nodes at the head of the list are removed as they are emptied.
61 typedef struct CqCharBuff CqCharBuff
;
63 CqCharBuff
*next
; /* The next node in the list of buffers */
64 char bytes
[GL_CQ_SIZE
]; /* The fixed size buffer of this node */
68 * Define the structure that is used to contain a list of character
72 ErrMsg
*err
; /* A buffer in which to record error messages */
73 FreeList
*bufmem
; /* A free-list of CqCharBuff structures */
75 CqCharBuff
*head
; /* The head of the list of output buffers */
76 CqCharBuff
*tail
; /* The tail of the list of output buffers */
78 int nflush
; /* The total number of characters that have been */
79 /* flushed from the start of the queue since */
80 /* _glq_empty_queue() was last called. */
81 int ntotal
; /* The total number of characters that have been */
82 /* appended to the queue since _glq_empty_queue() */
83 /* was last called. */
86 /*.......................................................................
87 * Create a new GlCharQueue object.
90 * return GlCharQueue * The new object, or NULL on error.
92 GlCharQueue
*_new_GlCharQueue(void)
94 GlCharQueue
*cq
; /* The object to be returned */
96 * Allocate the container.
98 cq
= malloc(sizeof(GlCharQueue
));
104 * Before attempting any operation that might fail, initialize the
105 * container at least up to the point at which it can safely be passed
106 * to del_GlCharQueue().
110 cq
->buffers
.head
= NULL
;
111 cq
->buffers
.tail
= NULL
;
112 cq
->nflush
= cq
->ntotal
= 0;
114 * Allocate a place to record error messages.
116 cq
->err
= _new_ErrMsg();
118 return _del_GlCharQueue(cq
);
120 * Allocate the freelist of CqCharBuff structures.
122 cq
->bufmem
= _new_FreeList(sizeof(CqCharBuff
), 1);
124 return _del_GlCharQueue(cq
);
128 /*.......................................................................
129 * Delete a GlCharQueue object.
132 * cq GlCharQueue * The object to be deleted.
134 * return GlCharQueue * The deleted object (always NULL).
136 GlCharQueue
*_del_GlCharQueue(GlCharQueue
*cq
)
139 cq
->err
= _del_ErrMsg(cq
->err
);
140 cq
->bufmem
= _del_FreeList(cq
->bufmem
, 1);
146 /*.......................................................................
147 * Append an array of n characters to a character queue.
150 * cq GlCharQueue * The queue to append to.
151 * chars const char * The array of n characters to be appended.
152 * n int The number of characters in chars[].
153 * write_fn GL_WRITE_FN * The function to call to output characters,
154 * or 0 to simply discard the contents of the
155 * queue. This will be called whenever the
156 * buffer becomes full. If it fails to release
157 * any space, the buffer will be extended.
158 * data void * Anonymous data to pass to write_fn().
160 * return int The number of characters successfully
161 * appended. This will only be < n on error.
163 int _glq_append_chars(GlCharQueue
*cq
, const char *chars
, int n
,
164 GlWriteFn
*write_fn
, void *data
)
166 int ndone
= 0; /* The number of characters appended so far */
168 * Check the arguments.
175 * The appended characters may have to be split between multiple
176 * buffers, so loop for each buffer.
179 int ntodo
; /* The number of characters remaining to be appended */
180 int nleft
; /* The amount of space remaining in cq->buffers.tail */
181 int nnew
; /* The number of characters to append to cq->buffers.tail */
183 * Compute the offset at which the next character should be written
184 * into the tail buffer segment.
186 int boff
= cq
->ntotal
% GL_CQ_SIZE
;
188 * Since we don't allocate a new buffer until we have at least one
189 * character to write into it, if boff is 0 at this point, it means
190 * that we hit the end of the tail buffer segment on the last append,
191 * so we need to allocate a new one.
193 * If allocating this new node will require a call to malloc(), as
194 * opposed to using a currently unused node in the freelist, first try
195 * flushing the current contents of the buffer to the terminal. When
196 * write_fn() uses blocking I/O, this stops the buffer size ever getting
197 * bigger than a single buffer node. When it is non-blocking, it helps
198 * to keep the amount of memory, but it isn't gauranteed to do so.
200 if(boff
== 0 && _idle_FreeListNodes(cq
->bufmem
) == 0) {
201 switch(_glq_flush_queue(cq
, write_fn
, data
)) {
204 case GLQ_FLUSH_AGAIN
:
205 errno
= 0; /* Don't confuse the caller */
208 return ndone
; /* Error */
210 boff
= cq
->ntotal
% GL_CQ_SIZE
;
213 * Since we don't allocate a new buffer until we have at least one
214 * character to write into it, if boff is 0 at this point, it means
215 * that we hit the end of the tail buffer segment on the last append,
216 * so we need to allocate a new one.
220 * Allocate the new node.
222 CqCharBuff
*node
= (CqCharBuff
*) _new_FreeListNode(cq
->bufmem
);
224 _err_record_msg(cq
->err
, "Insufficient memory to buffer output.",
229 * Initialize the node.
233 * Append the new node to the tail of the list.
236 cq
->buffers
.tail
->next
= node
;
238 cq
->buffers
.head
= node
;
239 cq
->buffers
.tail
= node
;
242 * How much room is there for new characters in the current tail node?
244 nleft
= GL_CQ_SIZE
- boff
;
246 * How many characters remain to be appended?
250 * How many characters should we append to the current tail node?
252 nnew
= nleft
< ntodo
? nleft
: ntodo
;
254 * Append the latest prefix of nnew characters.
256 memcpy(cq
->buffers
.tail
->bytes
+ boff
, chars
+ ndone
, nnew
);
261 * Return the count of the number of characters successfully appended.
266 /*.......................................................................
267 * Discard the contents of a queue of characters.
270 * cq GlCharQueue * The queue to clear.
272 void _glq_empty_queue(GlCharQueue
*cq
)
276 * Return all list nodes to their respective free-lists.
278 _rst_FreeList(cq
->bufmem
);
280 * Mark the lists as empty.
282 cq
->buffers
.head
= cq
->buffers
.tail
= NULL
;
283 cq
->nflush
= cq
->ntotal
= 0;
287 /*.......................................................................
288 * Return a count of the number of characters currently in the queue.
291 * cq GlCharQueue * The queue of interest.
293 * return int The number of characters in the queue.
295 int _glq_char_count(GlCharQueue
*cq
)
297 return (cq
&& cq
->buffers
.head
) ? (cq
->ntotal
- cq
->nflush
) : 0;
300 /*.......................................................................
301 * Write as many characters as possible from the start of a character
302 * queue via a given output callback function, removing those written
306 * cq GlCharQueue * The queue to write characters from.
307 * write_fn GL_WRITE_FN * The function to call to output characters,
308 * or 0 to simply discard the contents of the
310 * data void * Anonymous data to pass to write_fn().
312 * return GlFlushState The status of the flush operation:
313 * GLQ_FLUSH_DONE - The flush operation
314 * completed successfully.
315 * GLQ_FLUSH_AGAIN - The flush operation
316 * couldn't be completed
317 * on this call. Call this
318 * function again when the
319 * output channel can accept
321 * GLQ_FLUSH_ERROR Unrecoverable error.
323 GlqFlushState
_glq_flush_queue(GlCharQueue
*cq
, GlWriteFn
*write_fn
,
327 * Check the arguments.
331 return GLQ_FLUSH_ERROR
;
334 * If possible keep writing until all of the chained buffers have been
335 * emptied and removed from the list.
337 while(cq
->buffers
.head
) {
339 * Are we looking at the only node in the list?
341 int is_tail
= cq
->buffers
.head
== cq
->buffers
.tail
;
343 * How many characters more than an exact multiple of the buffer-segment
344 * size have been added to the buffer so far?
346 int nmodulo
= cq
->ntotal
% GL_CQ_SIZE
;
348 * How many characters of the buffer segment at the head of the list
349 * have been used? Note that this includes any characters that have
350 * already been flushed. Also note that if nmodulo==0, this means that
351 * the tail buffer segment is full. The reason for this is that we
352 * don't allocate new tail buffer segments until there is at least one
353 * character to be added to them.
355 int nhead
= (!is_tail
|| nmodulo
== 0) ? GL_CQ_SIZE
: nmodulo
;
357 * How many characters remain to be flushed from the buffer
358 * at the head of the list?
360 int nbuff
= nhead
- (cq
->nflush
% GL_CQ_SIZE
);
362 * Attempt to write this number.
364 int nnew
= write_fn(data
, cq
->buffers
.head
->bytes
+
365 cq
->nflush
% GL_CQ_SIZE
, nbuff
);
367 * Was anything written?
371 * Increment the count of the number of characters that have
372 * been flushed from the head of the queue.
376 * If we succeded in writing all of the contents of the current
377 * buffer segment, remove it from the queue.
381 * If we just emptied the last node left in the list, then the queue is
382 * now empty and should be reset.
385 _glq_empty_queue(cq
);
388 * Get the node to be removed from the head of the list.
390 CqCharBuff
*node
= cq
->buffers
.head
;
392 * Make the node that follows it the new head of the queue.
394 cq
->buffers
.head
= node
->next
;
396 * Return it to the freelist.
398 node
= (CqCharBuff
*) _del_FreeListNode(cq
->bufmem
, node
);
402 * If the write blocked, request that this function be called again
403 * when space to write next becomes available.
406 return GLQ_FLUSH_AGAIN
;
411 _err_record_msg(cq
->err
, "Error writing to terminal", END_ERR_MSG
);
412 return GLQ_FLUSH_ERROR
;
416 * To get here the queue must now be empty.
418 return GLQ_FLUSH_DONE
;
421 /*.......................................................................
422 * Return extra information (ie. in addition to that provided by errno)
423 * about the last error to occur in any of the public functions of this
427 * cq GlCharQueue * The container of the history list.
429 * return const char * A pointer to the internal buffer in which
430 * the error message is temporarily stored.
432 const char *_glq_last_error(GlCharQueue
*cq
)
434 return cq
? _err_get_msg(cq
->err
) : "NULL GlCharQueue argument";