LCOV - code coverage report
Current view: top level - app/shared/commands - help.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 83 0.0 %
Date: 2026-06-29 05:51:35 Functions: 0 6 0.0 %

          Line data    Source code
       1             : #include "../fd_config.h"
       2             : #include "../fd_action.h"
       3             : 
       4             : #include <string.h>
       5             : #include <unistd.h>
       6             : 
       7             : extern action_t * ACTIONS[];
       8             : 
       9           0 : #define HELP_ARG_INDENT (3UL)
      10           0 : #define HELP_ARG_GAP    (4UL)  /* spaces between the flag column and its description */
      11             : #define HELP_ARG_MAX    (64UL) /* most arguments any single action emits */
      12             : 
      13             : /* fd_action_help is the builder passed to an action's args_help
      14             :    callback.  The callback emits arguments by calling fd_action_help_arg,
      15             :    which simply records them here; fd_action_help_print then formats the
      16             :    collected arguments into an aligned column. */
      17             : 
      18             : struct fd_action_help {
      19             :   ulong cnt;
      20             :   struct {
      21             :     char const * name;
      22             :     char const * value;
      23             :     char const * description;
      24             :   } args[ HELP_ARG_MAX ];
      25             : };
      26             : 
      27             : void
      28             : fd_action_help_arg( fd_action_help_t * help,
      29             :                     char const *       name,
      30             :                     char const *       value,
      31           0 :                     char const *       description ) {
      32           0 :   if( FD_UNLIKELY( help->cnt>=HELP_ARG_MAX ) ) FD_LOG_ERR(( "too many help arguments (max %lu); increase HELP_ARG_MAX", HELP_ARG_MAX ));
      33           0 :   help->args[ help->cnt ].name        = name;
      34           0 :   help->args[ help->cnt ].value       = value;
      35           0 :   help->args[ help->cnt ].description = description;
      36           0 :   help->cnt++;
      37           0 : }
      38             : 
      39             : /* help_print_desc prints desc starting at the current column, wrapping
      40             :    any embedded newlines so continuation lines align under the first.
      41             :    col is the flag column width chosen for this command. */
      42             : 
      43             : static void
      44             : help_print_desc( char const * desc,
      45           0 :                  ulong        col ) {
      46           0 :   ulong pad = HELP_ARG_INDENT + col + HELP_ARG_GAP;
      47           0 :   for(;;) {
      48           0 :     char const * nl = strchr( desc, '\n' );
      49           0 :     if( FD_LIKELY( !nl ) ) {
      50           0 :       FD_LOG_STDOUT(( "%s\n", desc ));
      51           0 :       break;
      52           0 :     }
      53           0 :     FD_LOG_STDOUT(( "%.*s\n%*s", (int)(nl-desc), desc, (int)pad, "" ));
      54           0 :     desc = nl+1UL;
      55           0 :   }
      56           0 : }
      57             : 
      58             : /* help_print_args renders a collected set of arguments under the given
      59             :    section header, sizing the flag column to the widest flag so the
      60             :    descriptions line up.  Does nothing if help is empty. */
      61             : 
      62             : static void
      63             : help_print_args( char const *             header,
      64           0 :                  fd_action_help_t const * help ) {
      65           0 :   if( FD_UNLIKELY( !help->cnt ) ) return;
      66             : 
      67           0 :   FD_LOG_STDOUT(( "%s\n", header ));
      68             : 
      69           0 :   ulong col = 0UL;
      70           0 :   for( ulong i=0UL; i<help->cnt; i++ ) {
      71           0 :     ulong len = strlen( help->args[ i ].name );
      72           0 :     if( help->args[ i ].value ) len += 1UL + strlen( help->args[ i ].value ); /* space + value */
      73           0 :     if( len>col ) col = len;
      74           0 :   }
      75             : 
      76           0 :   for( ulong i=0UL; i<help->cnt; i++ ) {
      77           0 :     char flag[ 64 ];
      78           0 :     if( help->args[ i ].value ) FD_TEST( fd_cstr_printf_check( flag, sizeof( flag ), NULL, "%s %s", help->args[ i ].name, help->args[ i ].value ) );
      79           0 :     else                        FD_TEST( fd_cstr_printf_check( flag, sizeof( flag ), NULL, "%s",    help->args[ i ].name                        ) );
      80           0 :     FD_LOG_STDOUT(( "%*s%-*s%*s", (int)HELP_ARG_INDENT, "", (int)col, flag, (int)HELP_ARG_GAP, "" ));
      81           0 :     help_print_desc( help->args[ i ].description, col );
      82           0 :   }
      83           0 : }
      84             : 
      85             : /* help_print_derived_usage prints a "Usage" line derived from the
      86             :    action's arguments.  Used when the action does not supply an explicit
      87             :    .usage field.  Positional arguments are listed in order so required
      88             :    positionals are never hidden, followed by a trailing `[OPTIONS]`.  An
      89             :    action only needs to set .usage field when it has structure this
      90             :    simple default cannot express (e.g. a `<a|b|c>` subcommand choice). */
      91             : static void
      92             : help_print_derived_usage( action_t const *         action,
      93           0 :                           fd_action_help_t const * help ) {
      94           0 :   char usage[ 256 ];
      95           0 :   char * p   = fd_cstr_init( usage );
      96           0 :   char * end = usage + sizeof(usage);
      97             : 
      98           0 :   for( ulong i=0UL; i<help->cnt; i++ ) {
      99           0 :     char const * name = help->args[ i ].name;
     100           0 :     if( name[ 0 ]=='-' ) continue; /* a flag, covered by the [OPTIONS] below */
     101             : 
     102           0 :     char piece[ 96 ];
     103           0 :     ulong piece_len;
     104           0 :     if( help->args[ i ].value ) FD_TEST( fd_cstr_printf_check( piece, sizeof(piece), &piece_len, " %s %s", name, help->args[ i ].value ) );
     105           0 :     else                        FD_TEST( fd_cstr_printf_check( piece, sizeof(piece), &piece_len, " %s",    name                        ) );
     106           0 :     FD_TEST( piece_len < (ulong)(end-p) );
     107           0 :     p = fd_cstr_append_cstr( p, piece );
     108           0 :   }
     109             : 
     110           0 :   FD_TEST( 10UL < (ulong)(end-p) );
     111           0 :   p = fd_cstr_append_cstr( p, " [OPTIONS]" );
     112           0 :   fd_cstr_fini( p );
     113             : 
     114           0 :   FD_LOG_STDOUT(( "Usage: %s %s%s\n\n", FD_BINARY_NAME, action->name, usage ));
     115           0 : }
     116             : 
     117             : void
     118           0 : fd_action_help_print( action_t const * action ) {
     119           0 :   FD_LOG_STDOUT(( "%s\n", action->description ));
     120             : 
     121           0 :   if( FD_LIKELY( action->detail ) ) FD_LOG_STDOUT(( "\n%s\n", action->detail ));
     122             : 
     123           0 :   FD_LOG_STDOUT(( "\n" ));
     124             : 
     125           0 :   fd_action_help_t help[1] = {0};
     126           0 :   if( FD_LIKELY( action->args_help ) ) action->args_help( help );
     127             : 
     128           0 :   if( FD_LIKELY( action->usage ) ) FD_LOG_STDOUT(( "Usage: %s %s\n\n", FD_BINARY_NAME, action->usage ));
     129           0 :   else                             help_print_derived_usage( action, help );
     130             : 
     131           0 :   fd_action_help_t global[1] = {0};
     132           0 :   fd_global_options_help( global );
     133           0 :   help_print_args( "GLOBAL OPTIONS:", global );
     134             : 
     135           0 :   if( FD_LIKELY( help->cnt ) ) {
     136           0 :     FD_LOG_STDOUT(( "\n" ));
     137           0 :     help_print_args( "ARGUMENTS:", help );
     138           0 :   }
     139           0 : }
     140             : 
     141             : void
     142             : help_cmd_fn( args_t *   args   FD_PARAM_UNUSED,
     143           0 :              config_t * config FD_PARAM_UNUSED ) {
     144           0 :   FD_LOG_STDOUT(( "%s control binary\n\n", FD_APP_NAME ));
     145           0 :   FD_LOG_STDOUT(( "Usage: %s <SUBCOMMAND> [OPTIONS]\n\n", FD_BINARY_NAME ));
     146             : 
     147           0 :   fd_action_help_t global[1] = {0};
     148           0 :   fd_global_options_help( global );
     149           0 :   help_print_args( "OPTIONS:", global );
     150             : 
     151           0 :   FD_LOG_STDOUT(( "\nSUBCOMMANDS:\n" ));
     152           0 :   for( ulong i=0UL; ACTIONS[ i ]; i++ ) {
     153             :     FD_LOG_STDOUT(( "   %20s    %s\n", ACTIONS[ i ]->name, ACTIONS[ i ]->description ));
     154           0 :   }
     155           0 : }
     156             : 
     157             : action_t fd_action_help = {
     158             :   .name          = "help",
     159             :   .args          = NULL,
     160             :   .fn            = help_cmd_fn,
     161             :   .perm          = NULL,
     162             :   .description   = "Print this help message",
     163             :   .is_help       = 1,
     164             :   .is_immediate  = 1,
     165             :   .is_diagnostic = 1,
     166             : };

Generated by: LCOV version 1.14