blob: 1c28d18ed4b02d6947d0b6bf52ab48ccc656f168 [file] [log] [blame]
njnc44a6c22005-06-03 13:21:18 +00001
2/*--------------------------------------------------------------------*/
3/*--- Libc printing. m_libcprint.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
9
njn9f207462009-03-10 22:02:09 +000010 Copyright (C) 2000-2009 Julian Seward
njnc44a6c22005-06-03 13:21:18 +000011 jseward@acm.org
12
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29*/
30
njnc7561b92005-06-19 01:24:32 +000031#include "pub_core_basics.h"
sewardj4cfea4f2006-10-14 19:26:10 +000032#include "pub_core_vki.h"
njnc44a6c22005-06-03 13:21:18 +000033#include "pub_core_debuglog.h"
34#include "pub_core_libcbase.h"
njn132bfcc2005-06-04 19:16:06 +000035#include "pub_core_libcassert.h"
sewardj592ae092005-11-08 19:01:44 +000036#include "pub_core_libcfile.h" // VG_(write)(), VG_(write_socket)()
njnc44a6c22005-06-03 13:21:18 +000037#include "pub_core_libcprint.h"
sewardj592ae092005-11-08 19:01:44 +000038#include "pub_core_libcproc.h" // VG_(getpid)(), VG_(read_millisecond_timer()
njnc44a6c22005-06-03 13:21:18 +000039#include "pub_core_options.h"
njn24a6efb2005-06-20 03:36:51 +000040#include "valgrind.h" // For RUNNING_ON_VALGRIND
njnc44a6c22005-06-03 13:21:18 +000041
njnc44a6c22005-06-03 13:21:18 +000042
43/* ---------------------------------------------------------------------
44 Writing to file or a socket
45 ------------------------------------------------------------------ */
46
sewardj738856f2009-07-15 14:48:32 +000047/* The destination sinks for normal and XML output. These have their
48 initial values here; they are set to final values by
49 m_main.main_process_cmd_line_options(). See comment at the top of
50 that function for the associated logic. */
51OutputSink VG_(log_output_sink) = { 2, False }; /* 2 = stderr */
52OutputSink VG_(xml_output_sink) = { -1, False }; /* disabled */
53
njnc44a6c22005-06-03 13:21:18 +000054/* Do the low-level send of a message to the logging sink. */
sewardj738856f2009-07-15 14:48:32 +000055static
56void send_bytes_to_logging_sink ( OutputSink* sink, Char* msg, Int nbytes )
njnc44a6c22005-06-03 13:21:18 +000057{
sewardj738856f2009-07-15 14:48:32 +000058 if (sink->is_socket) {
59 Int rc = VG_(write_socket)( sink->fd, msg, nbytes );
60 if (rc == -1) {
61 // For example, the listener process died. Switch back to stderr.
62 sink->is_socket = False;
63 sink->fd = 2;
64 VG_(write)( sink->fd, msg, nbytes );
65 }
66 } else {
67 /* sink->fd could have been set to -1 in the various
sewardj6e31f802007-11-17 22:29:25 +000068 sys-wrappers for sys_fork, if --child-silent-after-fork=yes
69 is in effect. That is a signal that we should not produce
70 any more output. */
sewardj738856f2009-07-15 14:48:32 +000071 if (sink->fd >= 0)
72 VG_(write)( sink->fd, msg, nbytes );
njnc44a6c22005-06-03 13:21:18 +000073 }
74}
75
sewardj738856f2009-07-15 14:48:32 +000076
njnc44a6c22005-06-03 13:21:18 +000077/* ---------------------------------------------------------------------
78 printf() and friends
79 ------------------------------------------------------------------ */
80
sewardj738856f2009-07-15 14:48:32 +000081/* --------- printf --------- */
82
sewardj45f4e7c2005-09-27 19:20:21 +000083typedef
84 struct {
sewardj738856f2009-07-15 14:48:32 +000085 HChar buf[512];
86 Int buf_used;
87 OutputSink* sink;
sewardj45f4e7c2005-09-27 19:20:21 +000088 }
sewardj738856f2009-07-15 14:48:32 +000089 printf_buf_t;
bart1a0cb6a2008-04-14 16:35:32 +000090
njnc44a6c22005-06-03 13:21:18 +000091// Adds a single char to the buffer. When the buffer gets sufficiently
92// full, we write its contents to the logging sink.
sewardj738856f2009-07-15 14:48:32 +000093static void add_to__printf_buf ( HChar c, void *p )
njnc44a6c22005-06-03 13:21:18 +000094{
sewardj738856f2009-07-15 14:48:32 +000095 printf_buf_t *b = (printf_buf_t *)p;
njnc44a6c22005-06-03 13:21:18 +000096
sewardj738856f2009-07-15 14:48:32 +000097 if (b->buf_used > sizeof(b->buf) - 2 ) {
98 send_bytes_to_logging_sink( b->sink, b->buf, b->buf_used );
99 b->buf_used = 0;
njnc44a6c22005-06-03 13:21:18 +0000100 }
sewardj738856f2009-07-15 14:48:32 +0000101 b->buf[b->buf_used++] = c;
102 b->buf[b->buf_used] = 0;
103 tl_assert(b->buf_used < sizeof(b->buf));
104}
105
106static UInt vprintf_to_buf ( printf_buf_t* b,
107 const HChar *format, va_list vargs )
108{
109 UInt ret = 0;
110 if (b->sink->fd >= 0) {
111 ret = VG_(debugLog_vprintf)
112 ( add_to__printf_buf, b, format, vargs );
113 }
114 return ret;
115}
116
117static UInt vprintf_WRK ( OutputSink* sink,
118 const HChar *format, va_list vargs )
119{
120 printf_buf_t myprintf_buf
121 = { "", 0, sink };
122 UInt ret
123 = vprintf_to_buf(&myprintf_buf, format, vargs);
124 // Write out any chars left in the buffer.
125 if (myprintf_buf.buf_used > 0) {
126 send_bytes_to_logging_sink( myprintf_buf.sink,
127 myprintf_buf.buf,
128 myprintf_buf.buf_used );
129 }
130 return ret;
njnc44a6c22005-06-03 13:21:18 +0000131}
132
133UInt VG_(vprintf) ( const HChar *format, va_list vargs )
134{
sewardj738856f2009-07-15 14:48:32 +0000135 return vprintf_WRK( &VG_(log_output_sink), format, vargs );
njnc44a6c22005-06-03 13:21:18 +0000136}
137
138UInt VG_(printf) ( const HChar *format, ... )
139{
140 UInt ret;
141 va_list vargs;
njnc44a6c22005-06-03 13:21:18 +0000142 va_start(vargs, format);
143 ret = VG_(vprintf)(format, vargs);
144 va_end(vargs);
njnc44a6c22005-06-03 13:21:18 +0000145 return ret;
146}
147
sewardj738856f2009-07-15 14:48:32 +0000148UInt VG_(vprintf_xml) ( const HChar *format, va_list vargs )
149{
150 return vprintf_WRK( &VG_(xml_output_sink), format, vargs );
151}
152
153UInt VG_(printf_xml) ( const HChar *format, ... )
bart1a0cb6a2008-04-14 16:35:32 +0000154{
155 UInt ret;
156 va_list vargs;
bart1a0cb6a2008-04-14 16:35:32 +0000157 va_start(vargs, format);
sewardj738856f2009-07-15 14:48:32 +0000158 ret = VG_(vprintf_xml)(format, vargs);
bart1a0cb6a2008-04-14 16:35:32 +0000159 va_end(vargs);
bart1a0cb6a2008-04-14 16:35:32 +0000160 return ret;
161}
162
sewardj738856f2009-07-15 14:48:32 +0000163/* An exact clone of VG_(printf_xml), unfortunately. */
164UInt VG_(printf_xml_no_f_c) ( const HChar *format, ... )
njnc44a6c22005-06-03 13:21:18 +0000165{
sewardj738856f2009-07-15 14:48:32 +0000166 UInt ret;
167 va_list vargs;
168 va_start(vargs, format);
169 ret = VG_(vprintf_xml)(format, vargs);
170 va_end(vargs);
171 return ret;
172}
173
174
175/* --------- sprintf --------- */
176
177/* If we had an explicit buf structure here, it would contain only one
178 field, indicating where the next char is to go. So use p directly
179 for that, rather than having it be a pointer to a structure. */
180
181static void add_to__sprintf_buf ( HChar c, void *p )
182{
183 HChar** b = p;
184 *(*b)++ = c;
njnc44a6c22005-06-03 13:21:18 +0000185}
186
187UInt VG_(vsprintf) ( Char* buf, const HChar *format, va_list vargs )
188{
189 Int ret;
sewardj738856f2009-07-15 14:48:32 +0000190 HChar* sprintf_ptr = buf;
njnc44a6c22005-06-03 13:21:18 +0000191
192 ret = VG_(debugLog_vprintf)
sewardj738856f2009-07-15 14:48:32 +0000193 ( add_to__sprintf_buf, &sprintf_ptr, format, vargs );
194 add_to__sprintf_buf('\0', &sprintf_ptr);
njnc44a6c22005-06-03 13:21:18 +0000195
196 vg_assert(VG_(strlen)(buf) == ret);
197
198 return ret;
199}
200
201UInt VG_(sprintf) ( Char* buf, const HChar *format, ... )
202{
203 UInt ret;
204 va_list vargs;
njnc44a6c22005-06-03 13:21:18 +0000205 va_start(vargs,format);
206 ret = VG_(vsprintf)(buf, format, vargs);
207 va_end(vargs);
njnc44a6c22005-06-03 13:21:18 +0000208 return ret;
209}
210
sewardj45f4e7c2005-09-27 19:20:21 +0000211
sewardj738856f2009-07-15 14:48:32 +0000212/* --------- snprintf --------- */
213
sewardj45f4e7c2005-09-27 19:20:21 +0000214typedef
215 struct {
216 HChar* buf;
217 Int buf_size;
218 Int buf_used;
219 }
sewardj738856f2009-07-15 14:48:32 +0000220 snprintf_buf_t;
sewardj45f4e7c2005-09-27 19:20:21 +0000221
sewardj738856f2009-07-15 14:48:32 +0000222static void add_to__snprintf_buf ( HChar c, void* p )
sewardj45f4e7c2005-09-27 19:20:21 +0000223{
sewardj738856f2009-07-15 14:48:32 +0000224 snprintf_buf_t* b = p;
sewardj45f4e7c2005-09-27 19:20:21 +0000225 if (b->buf_size > 0 && b->buf_used < b->buf_size) {
226 b->buf[b->buf_used++] = c;
227 if (b->buf_used < b->buf_size)
228 b->buf[b->buf_used] = 0;
sewardj03e2bb82006-12-24 03:02:18 +0000229 else
230 b->buf[b->buf_size-1] = 0; /* pre: b->buf_size > 0 */
sewardj45f4e7c2005-09-27 19:20:21 +0000231 }
232}
233
234UInt VG_(vsnprintf) ( Char* buf, Int size, const HChar *format, va_list vargs )
235{
236 Int ret;
sewardj738856f2009-07-15 14:48:32 +0000237 snprintf_buf_t b;
sewardj45f4e7c2005-09-27 19:20:21 +0000238 b.buf = buf;
239 b.buf_size = size < 0 ? 0 : size;
240 b.buf_used = 0;
241
242 ret = VG_(debugLog_vprintf)
sewardj738856f2009-07-15 14:48:32 +0000243 ( add_to__snprintf_buf, &b, format, vargs );
sewardj45f4e7c2005-09-27 19:20:21 +0000244
245 return b.buf_used;
246}
247
248UInt VG_(snprintf) ( Char* buf, Int size, const HChar *format, ... )
249{
250 UInt ret;
251 va_list vargs;
sewardj45f4e7c2005-09-27 19:20:21 +0000252 va_start(vargs,format);
253 ret = VG_(vsnprintf)(buf, size, format, vargs);
254 va_end(vargs);
sewardj45f4e7c2005-09-27 19:20:21 +0000255 return ret;
256}
257
258
sewardj738856f2009-07-15 14:48:32 +0000259/* --------- vcbprintf --------- */
260
261void VG_(vcbprintf)( void(*char_sink)(HChar, void* opaque),
262 void* opaque,
263 const HChar* format, va_list vargs )
264{
265 (void) VG_(debugLog_vprintf)
266 ( char_sink, opaque, format, vargs );
267}
268
269
njnc44a6c22005-06-03 13:21:18 +0000270/* ---------------------------------------------------------------------
njn856c54e2005-06-26 18:43:40 +0000271 percentify()
272 ------------------------------------------------------------------ */
273
njn7a5915e2005-07-17 16:12:59 +0000274// Percentify n/m with d decimal places. Includes the '%' symbol at the end.
njn5eaff2f2005-09-25 19:11:45 +0000275// Right justifies in 'buf'.
276void VG_(percentify)(ULong n, ULong m, UInt d, Int n_buf, char buf[])
njn856c54e2005-06-26 18:43:40 +0000277{
278 Int i, len, space;
njn641e6162005-07-17 16:16:41 +0000279 ULong p1;
njn5eaff2f2005-09-25 19:11:45 +0000280 Char fmt[32];
njn856c54e2005-06-26 18:43:40 +0000281
njn641e6162005-07-17 16:16:41 +0000282 if (m == 0) {
njn5eaff2f2005-09-25 19:11:45 +0000283 // Have to generate the format string in order to be flexible about
284 // the width of the field.
njn8a7b41b2007-09-23 00:51:24 +0000285 VG_(sprintf)(fmt, "%%-%ds", n_buf);
njn5eaff2f2005-09-25 19:11:45 +0000286 // fmt is now "%<n_buf>s" where <d> is 1,2,3...
287 VG_(sprintf)(buf, fmt, "--%");
njn641e6162005-07-17 16:16:41 +0000288 return;
289 }
290
291 p1 = (100*n) / m;
njn856c54e2005-06-26 18:43:40 +0000292
293 if (d == 0) {
294 VG_(sprintf)(buf, "%lld%%", p1);
295 } else {
296 ULong p2;
297 UInt ex;
njn856c54e2005-06-26 18:43:40 +0000298 switch (d) {
299 case 1: ex = 10; break;
300 case 2: ex = 100; break;
301 case 3: ex = 1000; break;
302 default: VG_(tool_panic)("Currently can only handle 3 decimal places");
303 }
304 p2 = ((100*n*ex) / m) % ex;
305 // Have to generate the format string in order to be flexible about
306 // the width of the post-decimal-point part.
307 VG_(sprintf)(fmt, "%%lld.%%0%dlld%%%%", d);
308 // fmt is now "%lld.%0<d>lld%%" where <d> is 1,2,3...
309 VG_(sprintf)(buf, fmt, p1, p2);
310 }
311
312 len = VG_(strlen)(buf);
313 space = n_buf - len;
314 if (space < 0) space = 0; /* Allow for v. small field_width */
315 i = len;
316
317 /* Right justify in field */
318 for ( ; i >= 0; i--) buf[i + space] = buf[i];
319 for (i = 0; i < space; i++) buf[i] = ' ';
320}
321
sewardja11553a2005-07-19 12:17:05 +0000322
323/* ---------------------------------------------------------------------
sewardj592ae092005-11-08 19:01:44 +0000324 elapsed_wallclock_time()
sewardja11553a2005-07-19 12:17:05 +0000325 ------------------------------------------------------------------ */
326
sewardj592ae092005-11-08 19:01:44 +0000327/* Get the elapsed wallclock time since startup into buf, which must
328 16 chars long. This is unchecked. It also relies on the
329 millisecond timer having been set to zero by an initial read in
330 m_main during startup. */
sewardja11553a2005-07-19 12:17:05 +0000331
sewardj592ae092005-11-08 19:01:44 +0000332void VG_(elapsed_wallclock_time) ( /*OUT*/HChar* buf )
sewardja11553a2005-07-19 12:17:05 +0000333{
sewardj592ae092005-11-08 19:01:44 +0000334 UInt t, ms, s, mins, hours, days;
335
336 t = VG_(read_millisecond_timer)(); /* milliseconds */
337
338 ms = t % 1000;
339 t /= 1000; /* now in seconds */
340
341 s = t % 60;
342 t /= 60; /* now in minutes */
343
344 mins = t % 60;
345 t /= 60; /* now in hours */
346
347 hours = t % 24;
348 t /= 24; /* now in days */
349
350 days = t;
351
njn82baca72009-07-24 05:35:49 +0000352 VG_(sprintf)(buf, "%02u:%02u:%02u:%02u.%03u ", days, hours, mins, s, ms);
sewardja11553a2005-07-19 12:17:05 +0000353}
354
355
njn856c54e2005-06-26 18:43:40 +0000356/* ---------------------------------------------------------------------
njnc44a6c22005-06-03 13:21:18 +0000357 message()
358 ------------------------------------------------------------------ */
359
sewardj738856f2009-07-15 14:48:32 +0000360/* A buffer for accumulating VG_(message) style output. This is
361 pretty much the same as VG_(printf)'s scheme, with two differences:
362
363 * The message buffer persists between calls, so that multiple
364 calls to VG_(message) can build up output.
365
366 * Whenever the first character on a line is emitted, the
367 ==PID== style preamble is stuffed in before it.
368*/
369typedef
370 struct {
371 HChar buf[512+128];
372 Int buf_used;
373 Bool atLeft; /* notionally, is the next char position at the
374 leftmost column? */
375 /* Current message kind - changes from call to call */
376 VgMsgKind kind;
sewardj738856f2009-07-15 14:48:32 +0000377 /* destination */
378 OutputSink* sink;
379 }
380 vmessage_buf_t;
381
382static vmessage_buf_t vmessage_buf
sewardj8ddb93a2010-02-15 10:07:05 +0000383 = { "", 0, True, Vg_UserMsg, &VG_(log_output_sink) };
sewardj738856f2009-07-15 14:48:32 +0000384
385
386// Adds a single char to the buffer. We aim to have at least 128
387// bytes free in the buffer, so that it's always possible to emit
388// the preamble into the buffer if c happens to be the character
389// following a \n. When the buffer gets too full, we write its
390// contents to the logging sink.
391static void add_to__vmessage_buf ( HChar c, void *p )
392{
393 HChar tmp[64];
394 vmessage_buf_t* b = (vmessage_buf_t*)p;
395
396 vg_assert(b->buf_used >= 0 && b->buf_used < sizeof(b->buf)-128);
397
398 if (UNLIKELY(b->atLeft)) {
399 // insert preamble
400 HChar ch;
401 Int i, depth;
402
403 switch (b->kind) {
404 case Vg_UserMsg: ch = '='; break;
405 case Vg_DebugMsg: ch = '-'; break;
406 case Vg_DebugExtraMsg: ch = '+'; break;
407 case Vg_ClientMsg: ch = '*'; break;
408 default: ch = '?'; break;
409 }
410
411 // Print one '>' in front of the messages for each level of
412 // self-hosting being performed.
413 depth = RUNNING_ON_VALGRIND;
414 if (depth > 10)
415 depth = 10; // ?!?!
416 for (i = 0; i < depth; i++) {
417 b->buf[b->buf_used++] = '>';
418 }
419
420 b->buf[b->buf_used++] = ch;
421 b->buf[b->buf_used++] = ch;
422
423 if (VG_(clo_time_stamp)) {
424 VG_(memset)(tmp, 0, sizeof(tmp));
425 VG_(elapsed_wallclock_time)(tmp);
426 tmp[sizeof(tmp)-1] = 0;
427 for (i = 0; tmp[i]; i++)
428 b->buf[b->buf_used++] = tmp[i];
429 }
430
sewardj8ddb93a2010-02-15 10:07:05 +0000431 VG_(sprintf)(tmp, "%d", VG_(getpid)());
sewardj738856f2009-07-15 14:48:32 +0000432 tmp[sizeof(tmp)-1] = 0;
433 for (i = 0; tmp[i]; i++)
434 b->buf[b->buf_used++] = tmp[i];
435
436 b->buf[b->buf_used++] = ch;
437 b->buf[b->buf_used++] = ch;
438 b->buf[b->buf_used++] = ' ';
439
440 /* We can't possibly have stuffed 96 chars in merely as a result
441 of making the preamble (can we?) */
442 vg_assert(b->buf_used < sizeof(b->buf)-32);
443 }
444
445 b->buf[b->buf_used++] = c;
446 b->buf[b->buf_used] = 0;
447
448 if (b->buf_used >= sizeof(b->buf) - 128) {
449 send_bytes_to_logging_sink( b->sink, b->buf, b->buf_used );
450 b->buf_used = 0;
451 }
452
453 b->atLeft = c == '\n';
454}
455
456
njnc44a6c22005-06-03 13:21:18 +0000457UInt VG_(vmessage) ( VgMsgKind kind, const HChar* format, va_list vargs )
458{
sewardj738856f2009-07-15 14:48:32 +0000459 UInt ret;
njnc44a6c22005-06-03 13:21:18 +0000460
sewardj738856f2009-07-15 14:48:32 +0000461 /* Note (carefully) that the buf persists from call to call, unlike
462 with the other printf variants in earlier parts of this file. */
463 vmessage_buf_t* b = &vmessage_buf; /* shorthand for convenience */
464
465 /* We have to set this each call, so that the correct flavour
466 of preamble is emitted at each \n. */
467 b->kind = kind;
468
sewardj738856f2009-07-15 14:48:32 +0000469 ret = VG_(debugLog_vprintf) ( add_to__vmessage_buf,
470 b, format, vargs );
njnc44a6c22005-06-03 13:21:18 +0000471
sewardj738856f2009-07-15 14:48:32 +0000472 /* If the message finished exactly with a \n, then flush it at this
473 point. If not, assume more bits of the same line will turn up
474 in later messages, so don't bother to flush it right now. */
475
476 if (b->atLeft && b->buf_used > 0) {
477 send_bytes_to_logging_sink( b->sink, b->buf, b->buf_used );
478 b->buf_used = 0;
njnc44a6c22005-06-03 13:21:18 +0000479 }
480
sewardj738856f2009-07-15 14:48:32 +0000481 return ret;
njnc44a6c22005-06-03 13:21:18 +0000482}
483
barta0b6b2c2008-07-07 06:49:24 +0000484/* Send a simple single-part XML message. */
485UInt VG_(message_no_f_c) ( VgMsgKind kind, const HChar* format, ... )
486{
487 UInt count;
488 va_list vargs;
489 va_start(vargs,format);
490 count = VG_(vmessage) ( kind, format, vargs );
491 va_end(vargs);
492 return count;
493}
494
njnc44a6c22005-06-03 13:21:18 +0000495/* Send a simple single-part message. */
496UInt VG_(message) ( VgMsgKind kind, const HChar* format, ... )
497{
498 UInt count;
499 va_list vargs;
500 va_start(vargs,format);
501 count = VG_(vmessage) ( kind, format, vargs );
502 va_end(vargs);
503 return count;
504}
505
sewardj738856f2009-07-15 14:48:32 +0000506/* VG_(message) variants with hardwired first argument. */
507UInt VG_(umsg) ( const HChar* format, ... )
508{
509 UInt count;
510 va_list vargs;
511 va_start(vargs,format);
512 count = VG_(vmessage) ( Vg_UserMsg, format, vargs );
513 va_end(vargs);
514 return count;
515}
516
517UInt VG_(dmsg) ( const HChar* format, ... )
518{
519 UInt count;
520 va_list vargs;
521 va_start(vargs,format);
522 count = VG_(vmessage) ( Vg_DebugMsg, format, vargs );
523 va_end(vargs);
524 return count;
525}
526
527UInt VG_(emsg) ( const HChar* format, ... )
528{
529 UInt count;
530 va_list vargs;
531 va_start(vargs,format);
532 count = VG_(vmessage) ( Vg_DebugExtraMsg, format, vargs );
533 va_end(vargs);
534 return count;
535}
536
537/* Flush any output that has accumulated in vmessage_buf as a
538 result of previous calls to VG_(message) et al. */
539void VG_(message_flush) ( void )
540{
541 vmessage_buf_t* b = &vmessage_buf;
542 send_bytes_to_logging_sink( b->sink, b->buf, b->buf_used );
543 b->buf_used = 0;
544}
545
546
njnc44a6c22005-06-03 13:21:18 +0000547/*--------------------------------------------------------------------*/
548/*--- end ---*/
549/*--------------------------------------------------------------------*/
550