1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <klib/extern.h>
28 #include <klib/out.h>
29 #include <klib/status.h>
30 #include <klib/log.h>
31 #include <klib/debug.h>
32 #include <klib/text.h>
33 #include <klib/printf.h>
34 #include <klib/rc.h>
35 #include <klib/sort.h>
36 #include <sysalloc.h>
37 #include <atomic32.h>
38 
39 #include "writer-priv.h"
40 
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <os-native.h>
46 #include <assert.h>
47 
48 #include <os-native.h>
49 
50 static char wrt_app[32];
51 static size_t wrt_app_length;
52 static char wrt_vers[16];
53 static size_t wrt_vers_length;
54 
55 void* KWrt_DefaultWriterDataStdOut = NULL;
56 void* KWrt_DefaultWriterDataStdErr = NULL;
57 
58 typedef struct RCCreateLoc RCCreateLoc;
59 struct RCCreateLoc
60 {
61     const char *filename;
62     const char *function;
63     uint32_t lineno;
64     rc_t rc;
65 };
66 
67 static RCCreateLoc RC_loc_queue [ 3 ];
68 static atomic32_t RC_loc_reserve, RC_loc_written, RC_loc_read;
69 #define RC_LOC_QUEUE_SIZE ( sizeof RC_loc_queue / sizeof RC_loc_queue [ 0 ] )
70 #define RC_LOC_QUEUE_MASK ( RC_LOC_QUEUE_SIZE - 1 )
71 static bool reporting_unread = false;
72 
73 /*
74  *  "appname" [ IN ] - identity of executable, usually argv[0]
75  *
76  *  "vers" [ IN ] - 4-part version code: 0xMMmmrrrr, where
77  *      MM = major release
78  *      mm = minor release
79  *    rrrr = bug-fix release
80  */
KWrtInit(const char * appname,uint32_t vers)81 LIB_EXPORT rc_t CC KWrtInit( const char* appname, uint32_t vers )
82 {
83     rc_t rc;
84 
85     if ( appname == NULL )
86         return RC ( rcRuntime, rcLog, rcConstructing, rcString, rcNull );
87     if ( appname [ 0 ] == 0 )
88         return RC ( rcRuntime, rcLog, rcConstructing, rcString, rcEmpty );
89 
90     do
91     {
92         const char* progname;
93         const char* ext;
94         size_t progname_z;
95 
96         /* find whichever is last \ or / */
97         string_measure(appname, &progname_z);
98         progname = string_rchr(appname, progname_z, '/');
99         if( progname == NULL ) {
100             progname = appname;
101         } else {
102             progname++;
103             string_measure(progname, &progname_z);
104         }
105         appname = string_rchr(progname, progname_z, '\\');
106         if( appname == NULL ) {
107             appname = progname;
108         } else {
109             appname++;
110         }
111         string_measure(appname, &progname_z);
112 
113         ext = string_chr(appname, progname_z, '.');
114 
115         if( ext != NULL ) {
116             wrt_app_length = ext - appname;
117         } else {
118             wrt_app_length = progname_z;
119         }
120         if ( wrt_app_length >= sizeof(wrt_app) ) {
121             wrt_app_length = sizeof(wrt_app) - 1;
122         }
123         memmove(wrt_app, appname, wrt_app_length);
124         wrt_app[wrt_app_length] = '\0';
125 
126         rc = string_printf ( wrt_vers, sizeof wrt_vers, & wrt_vers_length,
127             "%.3V", vers );
128         assert ( rc == 0 );
129 
130         rc = KWrtSysInit(&KWrt_DefaultWriterDataStdOut, &KWrt_DefaultWriterDataStdErr);
131         if (rc) break;
132 
133         rc = KOutInit();
134         if (rc) break;
135 
136         rc = KLogInit();
137         if (rc) break;
138 
139         rc = KStsInit();
140         if (rc) break;
141 
142         rc = KDbgInit();
143     } while (0);
144 
145     return rc;
146 }
147 
simple_write(int fd,const void * buf,size_t count)148 LIB_EXPORT size_t CC simple_write( int fd, const void * buf, size_t count )
149 {
150     /* calls the platform-specific implementation ( $PLATFORM/syswriter.c ) */
151     return sys_simple_write( fd, buf, count );
152 }
153 
is_a_tty(int fd)154 LIB_EXPORT int CC is_a_tty( int fd )
155 {
156     /* calls the platform-specific implementation ( $PLATFORM/syswriter.c ) */
157     return sys_is_a_tty( fd );
158 }
159 
160 /*--------------------------------------------------------------------------
161  * nvp - name/value pair
162  */
163 static
wrt_nvp_cmp_func(const void * a,const void * b,void * ignored)164 int64_t CC wrt_nvp_cmp_func(const void *a, const void *b, void * ignored)
165 {
166     int i = 0;
167     const char *key = a;
168     const char *name = ( ( const wrt_nvp_t* ) b ) -> name;
169 
170     while(key[i] == name[i]) {
171         if( key[i] == '\0' || name[i] == '\0' ) {
172             break;
173         }
174         ++i;
175     }
176     /* treat \0 or right-paren as key terminator */
177     if( key[i] != 0 && key[i] != ')' ) {
178         return (int64_t)key[i] - (int64_t)name[i];
179     }
180     return (int64_t)0 - (int64_t)name[i];
181 }
182 
183 static
wrt_nvp_sort_func(const void * a,const void * b,void * ignored)184 int64_t CC wrt_nvp_sort_func(const void *a, const void *b, void * ignored)
185 {
186     const wrt_nvp_t *left = a;
187     const wrt_nvp_t *right = b;
188     return strcmp ( left -> name, right -> name );
189 }
190 
wrt_nvp_sort(size_t argc,wrt_nvp_t argv[])191 LIB_EXPORT void CC wrt_nvp_sort( size_t argc, wrt_nvp_t argv[])
192 {
193     if( argc > 1 ) {
194         ksort(argv, argc, sizeof(argv[0]), wrt_nvp_sort_func, NULL);
195     }
196 }
197 
wrt_nvp_find(size_t argc,const wrt_nvp_t argv[],const char * key)198 LIB_EXPORT const wrt_nvp_t* CC wrt_nvp_find( size_t argc, const wrt_nvp_t argv[], const char* key )
199 {
200     if( argc > 0 ) {
201         return kbsearch(key, argv, argc, sizeof(argv[0]), wrt_nvp_cmp_func, NULL);
202     }
203     return NULL;
204 }
205 
wrt_nvp_find_value(size_t argc,const wrt_nvp_t argv[],const char * key)206 LIB_EXPORT const char* CC wrt_nvp_find_value( size_t argc, const wrt_nvp_t argv[], const char* key )
207 {
208     if( argc > 0 ) {
209         const wrt_nvp_t* n = (const wrt_nvp_t*)kbsearch(key, argv, argc, sizeof(argv[0]), wrt_nvp_cmp_func, NULL);
210         if( n != NULL ) {
211             return n->value;
212         }
213     }
214     return NULL;
215 }
216 
217 static
RCLiteral(rc_t self,char * buffer,size_t bsize,size_t * num_writ)218 rc_t RCLiteral ( rc_t self, char *buffer, size_t bsize, size_t *num_writ )
219 {
220 #if ! _DEBUGGING && RECORD_RC_FILE_LINE
221     ( void ) GetRCLineno ();
222 #endif
223     return string_printf ( buffer, bsize, num_writ
224 #if _DEBUGGING
225         , "rc = %s:%u:$s:%u.%u.%u.%u.%u"
226         , GetRCFilename(), GetRCLineno (), GetRCFunction ()
227 #else
228         , "rc = %u.%u.%u.%u.%u"
229 #endif
230         , ( uint32_t ) GetRCModule ( self )
231         , ( uint32_t ) GetRCTarget ( self )
232         , ( uint32_t ) GetRCContext ( self )
233         , ( uint32_t ) GetRCObject ( self )
234         , ( uint32_t ) GetRCState ( self )
235     );
236 }
237 
RCExplain(rc_t rc,char * buffer,size_t bsize,size_t * num_writ)238 LIB_EXPORT rc_t CC RCExplain ( rc_t rc, char *buffer, size_t bsize, size_t *num_writ )
239 {
240     return RCExplain2
241         ( rc, buffer, bsize, num_writ, eRCExOpt_CompleteMsg );
242 }
243 
RCExplain2(rc_t rc,char * buffer,size_t bsize,size_t * num_writ,enum ERCExplain2Options options)244 LIB_EXPORT rc_t CC RCExplain2 ( rc_t rc, char *buffer, size_t bsize, size_t *num_writ,
245                                 enum ERCExplain2Options options )
246 {
247     bool noMessageIfNoError =
248         (options == eRCExOpt_NoMessageIfNoError ||
249          options == eRCExOpt_ObjAndStateOnlyIfError);
250     int len;
251     size_t total = 0;
252 
253     const char *mod = GetRCModuleText ( GetRCModule ( rc ) );
254     const char *targ = GetRCTargetText ( GetRCTarget ( rc ) );
255     const char *ctx = GetRCContextText ( GetRCContext ( rc ) );
256     const char *obj = GetRCObjectText ( GetRCObject ( rc ) );
257     const char *state = GetRCStateText ( GetRCState ( rc ) );
258 
259     assert( buffer && num_writ );
260 
261     *num_writ = 0;
262     if( rc == 0 && noMessageIfNoError ) {
263         buffer[0] = '\0';
264         return 0;
265     }
266 
267     /* English'ish formatting */
268 #if _DEBUGGING
269     {
270         const char *function = GetRCFunction ();
271         if ( function != NULL )
272         {
273             len = snprintf(buffer + total, bsize - total, "%s:%u:%s: ", GetRCFilename(), GetRCLineno (), function );
274             if( len < 0 || ( total + len ) >= bsize ) {
275                 return RCLiteral ( rc, buffer, bsize, num_writ );
276             }
277             total += len;
278         }
279     }
280 #elif RECORD_RC_FILE_LINE
281     ( void ) GetRCLineno ();
282 #endif
283     if( obj != NULL ) {
284         len = snprintf(buffer + total, bsize - total, "%s", obj);
285         if( len < 0 || ( total + len ) >= bsize ) {
286             return RCLiteral ( rc, buffer, bsize, num_writ );
287         }
288         total += len;
289     }
290     if( state != NULL ) {
291         len = snprintf(buffer + total, bsize - total, "%s%s", total ? " " : "", state);
292         if( len < 0 || ( total + len ) >= bsize ) {
293             return RCLiteral ( rc, buffer, bsize, num_writ );
294         }
295         total += len;
296     }
297     if( rc != 0 && options == eRCExOpt_CompleteMsg ) {
298         if( ctx != NULL ) {
299             len = snprintf ( buffer + total, bsize - total, "%swhile %s", total ? " " : "", ctx );
300             if ( len < 0 || ( total + len ) >= bsize ) {
301                 return RCLiteral ( rc, buffer, bsize, num_writ );
302             }
303             total += len;
304             if( targ != NULL ) {
305                 len = snprintf ( buffer + total, bsize - total, "%s%s", total ? " " : "", targ );
306                 if( len < 0 || ( total + len ) >= bsize ) {
307                     return RCLiteral ( rc, buffer, bsize, num_writ );
308                 }
309                 total += len;
310             }
311         } else if( targ != NULL ) {
312             len = snprintf ( buffer + total,
313                 bsize - total, "%swhile acting upon %s", total ? " " : "", targ );
314             if( len < 0 || ( total + len ) >= bsize ) {
315                 return RCLiteral ( rc, buffer, bsize, num_writ );
316             }
317             total += len;
318         }
319     }
320     if( mod != NULL && options == eRCExOpt_CompleteMsg ) {
321         len = snprintf(buffer + total, bsize - total, "%swithin %s module", total ? " " : "", mod);
322         if( len < 0 || ( total + len ) >= bsize ) {
323             return RCLiteral ( rc, buffer, bsize, num_writ );
324         }
325         total += len;
326     }
327     *num_writ = total;
328     return 0;
329 }
330 
331 /*--------------------------------------------------------------------------
332  * RC
333  */
334 
335 
336 /* GetRCModuleText
337  */
GetRCModuleText(enum RCModule mod)338 const char *GetRCModuleText ( enum RCModule mod )
339 {
340     if ( ( int ) mod < 0 || ( int ) mod >= ( int ) rcLastModule_v1_2 )
341         return "<INVALID-MODULE>";
342     return gRCModule_str [ ( int ) mod ];
343 }
344 
345 /* GetRCModuleIdxText
346  */
GetRCModuleIdxText(enum RCModule mod)347 const char *GetRCModuleIdxText ( enum RCModule mod )
348 {
349     if ( ( int ) mod < 0 || ( int ) mod >= ( int ) rcLastModule_v1_2 )
350         return "<INVALID-MODULE>";
351     return gRCModuleIdx_str [ ( int ) mod ];
352 }
353 
354 /* GetRCTargetText
355  */
GetRCTargetText(enum RCTarget targ)356 const char *GetRCTargetText ( enum RCTarget targ )
357 {
358     if ( ( int ) targ < 0 || ( int ) targ >= ( int ) rcLastTarget_v1_2 )
359         return "<INVALID-TARGET>";
360     return gRCTarget_str [ ( int ) targ ];
361 }
362 
363 /* GetRCTargetIdxText
364  */
GetRCTargetIdxText(enum RCTarget targ)365 const char *GetRCTargetIdxText ( enum RCTarget targ )
366 {
367     if ( ( int ) targ < 0 || ( int ) targ >= ( int ) rcLastTarget_v1_2 )
368         return "<INVALID-TARGET>";
369     return gRCTargetIdx_str [ ( int ) targ ];
370 }
371 
372 /* GetRCContextText
373  */
GetRCContextText(enum RCContext ctx)374 const char *GetRCContextText ( enum RCContext ctx )
375 {
376     if ( ( int ) ctx < 0 || ( int ) ctx >= ( int ) rcLastContext_v1_1 )
377         return "<INVALID-CONTEXT>";
378     return gRCContext_str [ ( int ) ctx ];
379 }
380 
381 /* GetRCContextIdxText
382  */
GetRCContextIdxText(enum RCContext ctx)383 const char *GetRCContextIdxText ( enum RCContext ctx )
384 {
385     if ( ( int ) ctx < 0 || ( int ) ctx >= ( int ) rcLastContext_v1_1 )
386         return "<INVALID-CONTEXT>";
387     return gRCContextIdx_str [ ( int ) ctx ];
388 }
389 
390 /* GetRCObjectText
391  */
GetRCObjectText(int obj)392 const char *GetRCObjectText ( int obj )
393 {
394     if ( ( int ) obj < 0 || ( int ) obj >= ( int ) rcLastObject_v1_2 )
395         return "<INVALID-OBJECT>";
396     if ( ( int ) obj < ( int ) rcLastTarget_v1_1 )
397         return gRCTarget_str [ ( int ) obj ];
398     return gRCObject_str [ ( int ) obj - ( int ) ( rcLastTarget_v1_1 - 1 ) ];
399 }
400 
401 /* GetRCObjectIdxText
402  */
GetRCObjectIdxText(int obj)403 const char *GetRCObjectIdxText ( int obj )
404 {
405     if ( ( int ) obj < 0 || ( int ) obj >= ( int ) rcLastObject_v1_2 )
406         return "<INVALID-OBJECT>";
407     if ( ( int ) obj < ( int ) rcLastTarget_v1_1 )
408         return gRCTargetIdx_str [ ( int ) obj ];
409     return gRCObjectIdx_str [ ( int ) obj - ( int ) ( rcLastTarget_v1_1 - 1 ) ];
410 }
411 
412 /* GetRCStateText
413  */
GetRCStateText(enum RCState state)414 const char *GetRCStateText ( enum RCState state )
415 {
416     if ( ( int ) state < 0 || ( int ) state >= ( int ) rcLastState_v1_1 )
417         return "<INVALID-STATE>";
418     return gRCState_str [ ( int ) state ];
419 }
420 
421 /* GetRCStateIdxText
422  */
GetRCStateIdxText(enum RCState state)423 const char *GetRCStateIdxText ( enum RCState state )
424 {
425     if ( ( int ) state < 0 || ( int ) state >= ( int ) rcLastState_v1_1 )
426         return "<INVALID-STATE>";
427     return gRCStateIdx_str [ ( int ) state ];
428 }
429 
430 static
read_rc_loc_head(void)431 uint32_t read_rc_loc_head ( void )
432 {
433     int32_t idx = atomic32_read ( & RC_loc_written );
434     if ( ! reporting_unread )
435     {
436         atomic32_set ( & RC_loc_read, idx );
437     }
438     return idx;
439 }
440 
441 static
get_rc_filename(uint32_t idx)442 const char *get_rc_filename ( uint32_t idx )
443 {
444     const char *p = RC_loc_queue [ idx & RC_LOC_QUEUE_MASK ] . filename;
445     if( p != NULL )
446     {
447         int i;
448         const char *sep;
449         const char *RC_filename = p;
450 #if WINDOWS
451         static char win_rc_filename [ 4096 ];
452         size_t w, len = string_copy_measure ( win_rc_filename, sizeof win_rc_filename - 1, p );
453         if ( len >= 2 && isalpha ( win_rc_filename [ 0 ] ) && win_rc_filename [ 1 ] == ':' )
454         {
455             win_rc_filename [ 1 ] = win_rc_filename [ 0 ];
456             win_rc_filename [ 0 ] = '/';
457         }
458         for ( w = 0; w < len; ++ w )
459         {
460             if ( win_rc_filename [ w ] == '\\' )
461                 win_rc_filename [ w ] = '/';
462         }
463         p = RC_filename = win_rc_filename;
464 #endif
465         if ( (p = strstr(RC_filename, "/interfaces/")) != NULL ||
466              (p = strstr(RC_filename, "/libs/")) != NULL ||
467              (p = strstr(RC_filename, "/services/")) != NULL ||
468              (p = strstr(RC_filename, "/tools/")) != NULL ||
469              (p = strstr(RC_filename, "/asm-trace/")) != NULL )
470         {
471             return p + 1;
472         }
473 
474         for ( i = 0, sep = strrchr ( p = RC_filename, '/' ); sep != NULL && i < 3; ++ i )
475         {
476             p = sep + 1;
477             sep = string_rchr ( RC_filename, sep - RC_filename, '/' );
478         }
479     }
480 
481     return p;
482 }
483 
GetRCFilename(void)484 LIB_EXPORT const char * CC GetRCFilename ( void )
485 {
486     return get_rc_filename ( read_rc_loc_head () );
487 }
488 
489 /* InsertSpace
490  *  inserts a division after current text
491  *
492  *  "spacer" [ IN, NULL OKAY ] - optional characters to insert
493  */
LogInsertSpace(const char * spacer,char * buffer,size_t bsize,size_t * num_writ)494 LIB_EXPORT rc_t CC LogInsertSpace(const char *spacer, char *buffer, size_t bsize, size_t *num_writ)
495 {
496     int len;
497 
498     if ( spacer == NULL )
499     {
500         if ( bsize < 2 )
501             return RC ( rcRuntime, rcLog, rcLogging, rcBuffer, rcInsufficient );
502         buffer [ 0 ] = ' ';
503         buffer [ 1 ] = 0;
504         * num_writ = 1;
505         return 0;
506     }
507 
508     len = snprintf ( buffer, bsize, "%s", spacer );
509 
510     * num_writ = len;
511 
512     if ( len < 0 || (size_t)len >= bsize )
513     {
514         if ( len < 0 )
515             * num_writ = 0;
516         return RC ( rcRuntime, rcLog, rcLogging, rcBuffer, rcInsufficient );
517     }
518 
519     return 0;
520 }
521 
LogAppName(char * buffer,size_t bsize,size_t * num_writ)522 LIB_EXPORT rc_t CC LogAppName(char *buffer, size_t bsize, size_t *num_writ)
523 {
524     if( wrt_app_length > bsize ) {
525         return RC(rcRuntime, rcLog, rcLogging, rcBuffer, rcInsufficient);
526     }
527     memmove(buffer, wrt_app, wrt_app_length);
528     *num_writ = wrt_app_length;
529     return 0;
530 }
531 
LogAppVersion(char * buffer,size_t bsize,size_t * num_writ)532 LIB_EXPORT rc_t CC LogAppVersion(char *buffer, size_t bsize, size_t *num_writ)
533 {
534     if( wrt_vers_length > bsize ) {
535         return RC(rcRuntime, rcLog, rcLogging, rcBuffer, rcInsufficient);
536     }
537     memmove(buffer, wrt_vers, wrt_vers_length);
538     *num_writ = wrt_vers_length;
539     return 0;
540 }
541 
LogFlush(const KWrtHandler * handler,const char * buffer,const size_t bsize)542 LIB_EXPORT rc_t CC LogFlush ( const KWrtHandler* handler, const char *buffer, const size_t bsize)
543 {
544     rc_t rc = 0;
545     size_t num_written;
546     size_t remaining;
547 
548     assert(handler != NULL);
549     assert(buffer != NULL);
550 
551     for(remaining = bsize; rc == 0 && remaining > 0; remaining -= num_written, buffer += num_written) {
552         rc = handler->writer(handler->data, buffer, remaining, &num_written);
553     }
554     return rc;
555 }
556 
557 static
get_rc_function(uint32_t idx)558 const char *get_rc_function ( uint32_t idx )
559 {
560     return RC_loc_queue [ idx & RC_LOC_QUEUE_MASK ] . function;
561 }
562 
GetRCFunction(void)563 LIB_EXPORT const char * CC GetRCFunction ( void )
564 {
565     return get_rc_function ( read_rc_loc_head () );
566 }
567 
568 static
get_rc_lineno(uint32_t idx)569 uint32_t get_rc_lineno ( uint32_t idx )
570 {
571     return RC_loc_queue [ idx & RC_LOC_QUEUE_MASK ] . lineno;
572 }
573 
GetRCLineno(void)574 LIB_EXPORT uint32_t CC GetRCLineno ( void )
575 {
576     return get_rc_lineno ( read_rc_loc_head () );
577 }
578 
579 static
get_rc_code(uint32_t idx)580 rc_t get_rc_code ( uint32_t idx )
581 {
582     return RC_loc_queue [ idx & RC_LOC_QUEUE_MASK ] . rc;
583 }
584 
SetRCFileFuncLine(rc_t rc,const char * filename,const char * funcname,uint32_t lineno)585 LIB_EXPORT rc_t CC SetRCFileFuncLine ( rc_t rc, const char *filename, const char *funcname, uint32_t lineno )
586 {
587     /* the limit based upon last guy successfully written */
588     int32_t lim = atomic32_read ( & RC_loc_written ) + RC_LOC_QUEUE_SIZE;
589 
590     /* try to reserve a slot for writing */
591     int32_t rsrv = atomic32_read_and_add_lt ( & RC_loc_reserve, 1, lim ) + 1;
592 
593     /* see if we got the reservation */
594     if ( rsrv <= lim )
595     {
596         uint32_t idx = rsrv & RC_LOC_QUEUE_MASK;
597         RC_loc_queue [ idx ] . filename = filename;
598         RC_loc_queue [ idx ] . function = funcname;
599         RC_loc_queue [ idx ] . lineno = lineno;
600         RC_loc_queue [ idx ] . rc = rc;
601         /* TBD - proper release sequence */
602         atomic32_set ( & RC_loc_written, rsrv );
603     }
604 
605     return rc;
606 }
607 
608 /* GetUnreadRCInfo
609  *  expected to be called after all threads are quiet
610  */
GetUnreadRCInfo(rc_t * rc,const char ** filename,const char ** funcname,uint32_t * lineno)611 LIB_EXPORT bool CC GetUnreadRCInfo ( rc_t *rc, const char **filename, const char **funcname, uint32_t *lineno )
612 {
613     int32_t last_writ;
614 
615     reporting_unread = true;
616 
617     /* these are not atomic, but the ordering is important */
618     last_writ = atomic32_read ( & RC_loc_written );
619     if ( last_writ > 0 )
620     {
621         /* this arrangement attempts to make the access to
622            RC_loc_read dependent upon access to RC_loc_written */
623         int32_t last_read = atomic32_read ( & RC_loc_read );
624         if ( last_read < last_writ )
625         {
626             /* check reserved slots */
627             int32_t rsrv = atomic32_read ( & RC_loc_reserve );
628 
629             /* adjust last read */
630             if ( last_writ - last_read > RC_LOC_QUEUE_SIZE )
631                 last_read = last_writ - RC_LOC_QUEUE_SIZE;
632 
633             /* any reserved rows must be considered overwritten */
634             last_read += rsrv - last_writ;
635 
636             /* these are the rows we can report */
637             if ( last_read < last_writ )
638             {
639                 int32_t idx = last_read + 1;
640                 atomic32_set ( & RC_loc_read, idx );
641 
642                 if ( filename != NULL )
643                     * filename = get_rc_filename ( idx );
644                 if ( funcname != NULL )
645                     * funcname = get_rc_function ( idx );
646                 if ( lineno != NULL )
647                     * lineno = get_rc_lineno ( idx );
648                 if ( rc != NULL )
649                     * rc = get_rc_code ( idx );
650 
651                 return true;
652             }
653         }
654     }
655     reporting_unread = false;
656     return false;
657 }
658