Login
Check-in [2a405470c0]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Proof-of-concept handling of SIGINT via fsl_cx_interrupt() by way of fcli. Currently only honored by fsl_repo_rebuild() but "should" eventually be honored by any unusually-long-running operations.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2a405470c0d32a84f8644de1b5c509635a7e78cb
User & Date: stephan 2021-11-18 17:40:56.695
Context
2021-11-18
18:03
Tighten up the interruption flag handling and fcli's inclusion (or not) of sigaction(). check-in: 2c0147b596 user: stephan tags: trunk
17:40
Proof-of-concept handling of SIGINT via fsl_cx_interrupt() by way of fcli. Currently only honored by fsl_repo_rebuild() but "should" eventually be honored by any unusually-long-running operations. check-in: 2a405470c0 user: stephan tags: trunk
16:31
A workaround for an as-yet-unexplained case of an attempt to rebuild the same artifict twice. Shows up on my pi4 but not my x86 system. check-in: 242c38c7b2 user: stephan tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to auto.def.
219
220
221
222
223
224
225











226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
\#if !defined(HAVE_STDINT_H)
\#  define HAVE_STDINT_H 1
\#endif
\#endif
/* _WIN32 */

$FSL_PLATFORM_CONFIG_H












\#endif
/* $incGuard */
"
    close $out
    puts "Generated $ofile."
    return
}
# end of --amal bootstrap config generation

wh-require-bash
cc-check-c11

cc-check-sizeof "void *"

if {![cc-check-includes zlib.h] ||
    ![cc-check-function-in-lib compress z]} {
    user-error "Missing functional zlib"
}
cc-check-function-in-lib iconv iconv

#if {![cc-check-functions iconv] &&
#    ![cc-check-function-in-lib iconv iconv]} {
#    user-error "Cannot find iconv(3) in libc or libiconv"
#}


########################################################################







>
>
>
>
>
>
>
>
>
>
>




















>







219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
\#if !defined(HAVE_STDINT_H)
\#  define HAVE_STDINT_H 1
\#endif
\#endif
/* _WIN32 */

$FSL_PLATFORM_CONFIG_H
\#if !defined(HAVE_SIGACTION)
\#  if defined(FSL_AMALGAMATION_BUILD)
\#    if defined(FSL_PLATFORM_IS_UNIX)
\#      define HAVE_SIGACTION 1
\#    else
\#      define HAVE_SIGACTION 0
\#    endif
\#  else
\#    define HAVE_SIGACTION 0
\#  endif
\#endif

\#endif
/* $incGuard */
"
    close $out
    puts "Generated $ofile."
    return
}
# end of --amal bootstrap config generation

wh-require-bash
cc-check-c11

cc-check-sizeof "void *"

if {![cc-check-includes zlib.h] ||
    ![cc-check-function-in-lib compress z]} {
    user-error "Missing functional zlib"
}
cc-check-function-in-lib iconv iconv
cc-check-functions sigaction fork
#if {![cc-check-functions iconv] &&
#    ![cc-check-function-in-lib iconv iconv]} {
#    user-error "Cannot find iconv(3) in libc or libiconv"
#}


########################################################################
491
492
493
494
495
496
497



498
499
500
501
502
503
504
505
506
507
508
\#if !defined(HAVE_STAT)
\#  define HAVE_STAT 0
\#endif
\#endif
/*_WIN32*/

$FSL_PLATFORM_CONFIG_H




\#endif
/* $incGuard */
"
close $out
puts "Generated $confH."

if {[opt-bool dump-defines]} {
    msg-result "--dump-defines is creating file: $DUMP_DEFINES_FILE"
    make-config-header $DUMP_DEFINES_FILE -auto {*}
}







>
>
>











503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
\#if !defined(HAVE_STAT)
\#  define HAVE_STAT 0
\#endif
\#endif
/*_WIN32*/

$FSL_PLATFORM_CONFIG_H
\#if !defined(HAVE_SIGACTION)
\#  define HAVE_SIGACTION [get-define HAVE_SIGACTION 0]
\#endif

\#endif
/* $incGuard */
"
close $out
puts "Generated $confH."

if {[opt-bool dump-defines]} {
    msg-result "--dump-defines is creating file: $DUMP_DEFINES_FILE"
    make-config-header $DUMP_DEFINES_FILE -auto {*}
}
Changes to f-apps/f-rebuild.c.
18
19
20
21
22
23
24

25
26
27
28
29
30
31
#include "fossil-scm/fossil-internal.h"

// Only for testing/debugging..
#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)


/***

For reference (noting that there are no plans to to implement all of
these) options...

$ fossil help rebuild







>







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "fossil-scm/fossil-internal.h"

// Only for testing/debugging..
#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)


/***

For reference (noting that there are no plans to to implement all of
these) options...

$ fossil help rebuild
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  if(!frs->qDeltas.stmt){
    rc = fsl_cx_prepare(frs->f, &frs->qDeltas,
                        "SELECT rid FROM delta WHERE srcid=?1/*%s()*/",
                        __func__);
  }else{
    fsl_stmt_reset(&frs->qDeltas);
  }
  
  while(0==rc && rid>0){
    //MARKER(("TODO: %s(rid=%d)\n", __func__, (int)rid));
    if(blobSize != (int64_t)content->used){
      /* Fix [blob.size] field if needed. (Why would this ever
         be needed?) */
      rc = fsl_stmt_bind_step(&frs->qSize, "IR", (int64_t)content->used, rid);
      if(rc){







<







306
307
308
309
310
311
312

313
314
315
316
317
318
319
  if(!frs->qDeltas.stmt){
    rc = fsl_cx_prepare(frs->f, &frs->qDeltas,
                        "SELECT rid FROM delta WHERE srcid=?1/*%s()*/",
                        __func__);
  }else{
    fsl_stmt_reset(&frs->qDeltas);
  }

  while(0==rc && rid>0){
    //MARKER(("TODO: %s(rid=%d)\n", __func__, (int)rid));
    if(blobSize != (int64_t)content->used){
      /* Fix [blob.size] field if needed. (Why would this ever
         be needed?) */
      rc = fsl_stmt_bind_step(&frs->qSize, "IR", (int64_t)content->used, rid);
      if(rc){
331
332
333
334
335
336
337

338
339
340
341
342
343
344
    while(0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&frs->qDeltas)){
      fsl_id_t const cid = fsl_stmt_g_id(&frs->qDeltas, 0);
      if(!fsl_id_bag_contains(&frs->idsDone, cid)){
        rc = fsl_id_bag_insert(&idsChildren, cid);
      }
    }
    fsl_stmt_reset(&frs->qDeltas);

    if(rc) break;

    fsl_size_t const nChild = fsl_id_bag_count(&idsChildren);
    if(fsl_id_bag_contains(&frs->idsDone, rid)){
      /* Kludge! This check should not be necessary. Testing with the
         libfossil repo, this check is not required on the main x86
         dev machine but is on a Raspberry Pi 4, for reasons







>







331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
    while(0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&frs->qDeltas)){
      fsl_id_t const cid = fsl_stmt_g_id(&frs->qDeltas, 0);
      if(!fsl_id_bag_contains(&frs->idsDone, cid)){
        rc = fsl_id_bag_insert(&idsChildren, cid);
      }
    }
    fsl_stmt_reset(&frs->qDeltas);
    rc = INTCHECK rc;
    if(rc) break;

    fsl_size_t const nChild = fsl_id_bag_count(&idsChildren);
    if(fsl_id_bag_contains(&frs->idsDone, rid)){
      /* Kludge! This check should not be necessary. Testing with the
         libfossil repo, this check is not required on the main x86
         dev machine but is on a Raspberry Pi 4, for reasons
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
       deconstruct, performing different work for each. We're skipping
       the deconstruct option for now but may want to add it
       later. See fossil's rebuild.c:rebuild_step(). Note that
       deconstruct is not a capability intended for normal client
       use. It's primarily for testing of fossil itself. */
    fsl_deck_init(frs->f, &deck, FSL_SATYPE_ANY);
    //MARKER(("rid=%d\n", (int)rid));
    rc = fsl_deck_parse2(&deck, &deckContent, rid)
      /* But isn't it okay if rid is not an artifact? */;
    switch(rc){
      case FSL_RC_SYNTAX:
        /* assume deck is not an artifact */
        fsl_cx_err_reset(frs->f);
        rc = 0;
        /* Note that fossil ignores this case and continues processing the







|







365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
       deconstruct, performing different work for each. We're skipping
       the deconstruct option for now but may want to add it
       later. See fossil's rebuild.c:rebuild_step(). Note that
       deconstruct is not a capability intended for normal client
       use. It's primarily for testing of fossil itself. */
    fsl_deck_init(frs->f, &deck, FSL_SATYPE_ANY);
    //MARKER(("rid=%d\n", (int)rid));
    rc = INTCHECK fsl_deck_parse2(&deck, &deckContent, rid)
      /* But isn't it okay if rid is not an artifact? */;
    switch(rc){
      case FSL_RC_SYNTAX:
        /* assume deck is not an artifact */
        fsl_cx_err_reset(frs->f);
        rc = 0;
        /* Note that fossil ignores this case and continues processing the
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405

406
407
408
409
410
411
412
        if(0==rc){
          rid = 0;
          continue;
        }
#endif
        break;
      case 0:
        rc = fsl__deck_crosslink(&deck);
        break;
      default:
#if 0
        MARKER(("err=%s for rid=%d content=\n%.*s\n", fsl_rc_cstr(rc), (int)rid,
                (int)deckContent.used, (char const *)deckContent.mem));
#endif
        break;
    }
    fsl_buffer_clear(&deckContent);

    if(0==rc && 0==(rc = frs->f->interrupted)){
      rc = fsl__rebuild_step_done( frs, rid );
    }
    if(rc) break;
    /* Process all dependent deltas recursively... */
    doChildren:
    rid = 0;







|









>







390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
        if(0==rc){
          rid = 0;
          continue;
        }
#endif
        break;
      case 0:
        rc = INTCHECK fsl__deck_crosslink(&deck);
        break;
      default:
#if 0
        MARKER(("err=%s for rid=%d content=\n%.*s\n", fsl_rc_cstr(rc), (int)rid,
                (int)deckContent.used, (char const *)deckContent.mem));
#endif
        break;
    }
    fsl_buffer_clear(&deckContent);
    rc = INTCHECK 0;
    if(0==rc && 0==(rc = frs->f->interrupted)){
      rc = fsl__rebuild_step_done( frs, rid );
    }
    if(rc) break;
    /* Process all dependent deltas recursively... */
    doChildren:
    rid = 0;
450
451
452
453
454
455
456
457
458

459
460
461
462
463
464
465
            rc = fsl_cx_err_set(frs->f, rc,
                                "Error applying delta #%" FSL_ID_T_PFMT
                                " to parent #%" FSL_ID_T_PFMT, cid, rid);
          }
          goto outro;
        }
        if(i<nChild){
          rc = fsl__rebuild_step(frs, cid, sz, &next);
          assert(!next.mem);

        }else{
          /* Tail recursion */
          rid = cid;
          blobSize = sz;
          fsl_buffer_clear(content);
          *content = next/*transfer ownership*/;
        }







|

>







452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
            rc = fsl_cx_err_set(frs->f, rc,
                                "Error applying delta #%" FSL_ID_T_PFMT
                                " to parent #%" FSL_ID_T_PFMT, cid, rid);
          }
          goto outro;
        }
        if(i<nChild){
          rc = INTCHECK fsl__rebuild_step(frs, cid, sz, &next);
          assert(!next.mem);
          rc = INTCHECK rc;
        }else{
          /* Tail recursion */
          rid = cid;
          blobSize = sz;
          fsl_buffer_clear(content);
          *content = next/*transfer ownership*/;
        }
712
713
714
715
716
717
718






719
720
721
722
723
724
725
726
727
728
729
730

731
732
733
734
735
736
737
738
739
740
   result codes from the db layer). It runs in a transaction and will
   roll back on non-0, so it "shouldn't" leave a mess on error. If
   opt->dryRun is true then it also rolls back the transaction but it
   is not treated as an error.

   During the rebuild process opt->callback will, if it is not NULL,
   be called to provide feedback on its progress.






*/
int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt);
int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt){
  int rc = 0;
  fsl_db * const db = fsl_needs_repo(f);
  if(!db) return rc;
  rc = fsl_cx_transaction_begin(f);
  if(0==rc){
    rc = fsl__rebuild(f, opt);
    int const rc2 = fsl_cx_transaction_end(f, opt->dryRun || rc!=0);
    if(0==rc && 0!=rc2) rc = rc2;
  }

  return rc;
}


static int fsl_rebuild_f_my(fsl_rebuild_step const * const state){
  if(state->stepNumber){
    f_out("\r%" PRIu32 " of %" PRIu32,
          state->stepNumber, state->artifactCount);
  }else{
    f_out("\n");







>
>
>
>
>
>












>


<







715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742

743
744
745
746
747
748
749
   result codes from the db layer). It runs in a transaction and will
   roll back on non-0, so it "shouldn't" leave a mess on error. If
   opt->dryRun is true then it also rolls back the transaction but it
   is not treated as an error.

   During the rebuild process opt->callback will, if it is not NULL,
   be called to provide feedback on its progress.

   This operation honors interruption via fsl_cx_interrupt() but, due
   to intricacies of timing, it's possible that a triggered interrupt
   gets trumped by another error which happens while the check for the
   interrupt flag is pending. In both cases this function could return
   a non-0 code, though.
*/
int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt);
int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt){
  int rc = 0;
  fsl_db * const db = fsl_needs_repo(f);
  if(!db) return rc;
  rc = fsl_cx_transaction_begin(f);
  if(0==rc){
    rc = fsl__rebuild(f, opt);
    int const rc2 = fsl_cx_transaction_end(f, opt->dryRun || rc!=0);
    if(0==rc && 0!=rc2) rc = rc2;
  }
  fsl_cx_interrupt(f, 0, NULL);
  return rc;
}


static int fsl_rebuild_f_my(fsl_rebuild_step const * const state){
  if(state->stepNumber){
    f_out("\r%" PRIu32 " of %" PRIu32,
          state->stepNumber, state->artifactCount);
  }else{
    f_out("\n");
787
788
789
790
791
792
793
794

795
796
797
798
799
800
801
802
  if(!quiet){
    ropt.callback = fsl_rebuild_f_my;
    ropt.callbackState = NULL;
  }
  fsl_timer_start(&timer);
  rc = fsl_repo_rebuild(fcli_cx(), &ropt);
  {
    f_out("Work took a total of %f ms of CPU time.\n",

          (double)(fsl_timer_reset(&timer) / 1000.0));
    fcli_dump_cache_metrics();
  }

  f_out("\n");
  end:
  return fcli_end_of_main(rc);
}







|
>








796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
  if(!quiet){
    ropt.callback = fsl_rebuild_f_my;
    ropt.callbackState = NULL;
  }
  fsl_timer_start(&timer);
  rc = fsl_repo_rebuild(fcli_cx(), &ropt);
  {
    f_out("%sWork took a total of %f ms of CPU time.\n",
          ropt.callback ? "\n" : "",
          (double)(fsl_timer_reset(&timer) / 1000.0));
    fcli_dump_cache_metrics();
  }

  f_out("\n");
  end:
  return fcli_end_of_main(rc);
}
Changes to include/fossil-scm/fossil-core.h.
2090
2091
2092
2093
2094
2095
2096
2097

2098
2099
2100
2101



2102
2103




2104
2105
2106
2107
2108
2109
2110
2111
2112
2113

2114
2115






2116
2117
2118
2119
2120
2121
2122
   @see fsl_confirm_callback_f
   @see fsl_cx_confirmer()
*/
FSL_EXPORT int fsl_cx_confirm(fsl_cx * const f, fsl_confirm_detail const * detail,
                              fsl_confirm_response *outAnswer);

/**
   Sets f's is-interrupted flag. This error flag is separate from f's

   normal error state and is _not_ cleared by fsl_cx_err_reset(). To
   clear the interrupted flag, pass 0 as the 2nd argument and NULL as
   the 3rd. This flag _is_ fetched by fsl_cx_err_get() but does not
   have an associated error message.




   Returns code.





   Results are undefined if this function is called twice
   concurrently.  i.e. all calls must come from a single
   thread. Results are also undefined if it is called while f is in
   its finalization phase.

   ACHTUNG: this is new as of 2021-11-18 and is not yet widely honored
   within the API.

   @see fsl_cx_interrupted()

*/
FSL_EXPORT int fsl_cx_interrupt(fsl_cx * const f, int code);







/**
   If f's is-interrupted flag is set, this function returns its
   value. Note that there is inherently a race condition when calling
   fsl_cx_interrupt() (to set the flag) from another thread (e.g.  a
   UI thread while showing a progress indicator).
*/







|
>
|
|
|
|
>
>
>

|
>
>
>
>










>

|
>
>
>
>
>
>







2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
   @see fsl_confirm_callback_f
   @see fsl_cx_confirmer()
*/
FSL_EXPORT int fsl_cx_confirm(fsl_cx * const f, fsl_confirm_detail const * detail,
                              fsl_confirm_response *outAnswer);

/**
   Sets f's is-interrupted flag and its error state. The
   is-interrupted flag is separate from f's normal error state and is
   _not_ cleared by fsl_cx_err_reset(). To clear the interrupted flag,
   call this function with values of 0 and NULL for the 2nd and 3rd
   arguments, respective. This flag _is_ fetched by fsl_cx_err_get()
   but it is possible for any error message provided via this routine
   to be overwritten or reset by another routine before the
   interrupted flag can be acted upon, whereas the interrupted flag
   itself can only be modified by this routine.

   Returns its 2nd argument on success or FSL_RC_OOM if given a
   formatted string and allocation of it fails.

   If passed a code of 0, the is-interrupted flag is reset but the
   general error state is not.

   Results are undefined if this function is called twice
   concurrently.  i.e. all calls must come from a single
   thread. Results are also undefined if it is called while f is in
   its finalization phase.

   ACHTUNG: this is new as of 2021-11-18 and is not yet widely honored
   within the API.

   @see fsl_cx_interrupted()
   @see fsl_cx_interruptv()
*/
FSL_EXPORT int fsl_cx_interrupt(fsl_cx * const f, int code,
                                const char * fmt, ...);

/**
   The va_list counterpart of fsl_cx_interrupt().
*/
FSL_EXPORT int fsl_cx_interruptv(fsl_cx * const f, int code, char const * fmt, va_list args);

/**
   If f's is-interrupted flag is set, this function returns its
   value. Note that there is inherently a race condition when calling
   fsl_cx_interrupt() (to set the flag) from another thread (e.g.  a
   UI thread while showing a progress indicator).
*/
Changes to src/cli.c.
657
658
659
660
661
662
663





















664
665
666
667
668
669
670
671







672
673
674
675
676
677
678
    checking in an allocates-often API.
 */
static const fsl_allocator fcli_allocator = {
fsl_realloc_f_failing,
NULL/*state*/
};






















void fcli_pre_setup(void){
  static int run = 0;
  if(run++) return;
  fslAllocOrig = fsl_lib_configurable.allocator;
  fsl_lib_configurable.allocator = fcli_allocator
    /* This MUST be done BEFORE the fsl API allocates
       ANY memory! */;
  atexit(fcli_shutdown);







}
/**
   oldMode must be true if fcli.cliFlags is NULL, else false.
*/
static int fcli_setup_common1(bool oldMode, int argc, char const * const *argv){
  static char once = 0;
  int rc = 0;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








>
>
>
>
>
>
>







657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
    checking in an allocates-often API.
 */
static const fsl_allocator fcli_allocator = {
fsl_realloc_f_failing,
NULL/*state*/
};

#define FCLI_USE_SIGNALS HAVE_SIGACTION

#if FCLI_USE_SIGNALS
#include <signal.h> /* sigaction(), if our feature macros are set right */
/**
   SIGINT handler which calls fsl_cx_interrupt().
*/
static void fcli__sigc_handler(int s){
  static fsl_cx * f = 0;
  if(f) return/*disable concurrent interruption*/;
  f = fcli_cx();
  if(f){
    f_out("^C\n");
    fsl_cx_interrupt(f, FSL_RC_INTERRUPTED,
                     "Interrupted by signal #%d.", s);
    f = NULL;
  }
}
#endif
/* ^^^ FCLI_USE_SIGNALS */

void fcli_pre_setup(void){
  static int run = 0;
  if(run++) return;
  fslAllocOrig = fsl_lib_configurable.allocator;
  fsl_lib_configurable.allocator = fcli_allocator
    /* This MUST be done BEFORE the fsl API allocates
       ANY memory! */;
  atexit(fcli_shutdown);
#if FCLI_USE_SIGNALS
  struct sigaction sigIntHandler;
  sigIntHandler.sa_handler = fcli__sigc_handler;
  sigemptyset(&sigIntHandler.sa_mask);
  sigIntHandler.sa_flags = 0;
  sigaction(SIGINT, &sigIntHandler, NULL);
#endif
}
/**
   oldMode must be true if fcli.cliFlags is NULL, else false.
*/
static int fcli_setup_common1(bool oldMode, int argc, char const * const *argv){
  static char once = 0;
  int rc = 0;
811
812
813
814
815
816
817




818
819
820
821
822
823
824
825
826
827
828
829

830


831
832
833
834
835
836
837
  return fcli_setup_v2(argc, argv, NULL, NULL);
}

int fcli_err_report2(bool clear, char const * file, int line){
  int errRc = 0;
  char const * msg = NULL;
  errRc = fsl_error_get( fcli__error, &msg, NULL );




  if(FCLI_RC_HELP==errRc){
    errRc = 0;
  }else if(errRc || msg){
    if(fcli.clientFlags.verbose>0){
      fcli_printf("%s %s:%d: ERROR #%d (%s): %s\n",
                  fcli.appName,
                  file, line, errRc, fsl_rc_cstr(errRc), msg);
    }else{
      fcli_printf("%s: ERROR #%d (%s): %s\n",
                  fcli.appName, errRc, fsl_rc_cstr(errRc), msg);
    }
  }

  if(clear) fcli_err_reset();


  return errRc;
}


const char * fcli_next_arg(bool remove){
  const char * rc = (fcli.argc>0) ? fcli.argv[0] : NULL;
  if(rc && remove){







>
>
>
>












>
|
>
>







839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
  return fcli_setup_v2(argc, argv, NULL, NULL);
}

int fcli_err_report2(bool clear, char const * file, int line){
  int errRc = 0;
  char const * msg = NULL;
  errRc = fsl_error_get( fcli__error, &msg, NULL );
  if(!errRc && fcli.f && fcli.f->interrupted){
    errRc = fcli.f->interrupted;
    msg = "Interrupted.";
  }
  if(FCLI_RC_HELP==errRc){
    errRc = 0;
  }else if(errRc || msg){
    if(fcli.clientFlags.verbose>0){
      fcli_printf("%s %s:%d: ERROR #%d (%s): %s\n",
                  fcli.appName,
                  file, line, errRc, fsl_rc_cstr(errRc), msg);
    }else{
      fcli_printf("%s: ERROR #%d (%s): %s\n",
                  fcli.appName, errRc, fsl_rc_cstr(errRc), msg);
    }
  }
  if(clear){
    fcli_err_reset();
    if(fcli.f) fsl_cx_interrupt(fcli.f, 0, NULL);
  }
  return errRc;
}


const char * fcli_next_arg(bool remove){
  const char * rc = (fcli.argc>0) ? fcli.argv[0] : NULL;
  if(rc && remove){
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295

void fcli_dump_cache_metrics(void){
  fsl_cx * const f = fcli.f;
  if(!f) return;
  f_out("fsl_cx::cache::mcache hits = %u misses = %u\n",
        f->cache.mcache.hits,
        f->cache.mcache.misses);
  f_out("fsl_cx::cache::blobContent hits = %u misses = %u. "
        "Entry count=%u totaling %u byte(s)\n",
        f->cache.blobContent.metrics.hits,
        f->cache.blobContent.metrics.misses,
        f->cache.blobContent.used,
        f->cache.blobContent.szTotal);
}

#undef FCLI_V3
#undef fcli_empty_m
#undef fcli__error
#undef MARKER








|










>
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
void fcli_dump_cache_metrics(void){
  fsl_cx * const f = fcli.f;
  if(!f) return;
  f_out("fsl_cx::cache::mcache hits = %u misses = %u\n",
        f->cache.mcache.hits,
        f->cache.mcache.misses);
  f_out("fsl_cx::cache::blobContent hits = %u misses = %u. "
        "Entry count=%u totaling %u byte(s).\n",
        f->cache.blobContent.metrics.hits,
        f->cache.blobContent.metrics.misses,
        f->cache.blobContent.used,
        f->cache.blobContent.szTotal);
}

#undef FCLI_V3
#undef fcli_empty_m
#undef fcli__error
#undef MARKER
#undef FCLI_USE_SIGNALS
Changes to src/cx.c.
2028
2029
2030
2031
2032
2033
2034










2035





2036
2037
2038
2039
2040
2041
2042
2043
  goto end;
}

bool fsl_cx_has_ckout(fsl_cx const * const f ){
  return f->ckout.dir ? true : false;
}











int fsl_cx_interrupt(fsl_cx * const f, int code){





  return f->interrupted = code;
}

int fsl_cx_interrupted(fsl_cx const * const f){
  return f->interrupted;
}

#if 0







>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
|







2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
  goto end;
}

bool fsl_cx_has_ckout(fsl_cx const * const f ){
  return f->ckout.dir ? true : false;
}

int fsl_cx_interruptv(fsl_cx * const f, int code, char const * fmt, va_list args){
  if(code){
    f->interrupted = code;
    code = fsl_cx_err_setv(f, code, fmt, args);
  }else{
    f->interrupted = 0;
  }
  return code;
}

int fsl_cx_interrupt(fsl_cx * const f, int code, char const * fmt, ...){
  int rc;
  va_list args;
  va_start(args,fmt);
  rc = fsl_cx_interruptv(f, code, fmt, args);
  va_end(args);
  return rc;
}

int fsl_cx_interrupted(fsl_cx const * const f){
  return f->interrupted;
}

#if 0