+#define FORMAT_STRING " %s --%s%s%s%s%s%s%s "
+ /* short option ------' | | | | | | |
+ * option name ------------' | | | | | |
+ * <bra ---------------------' | | | | |
+ * option type or "" ----------' | | | |
+ * ket> -------------------------' | | |
+ * padding spaces -----------------' | |
+ * comment --------------------------' |
+ * comment suffix ---------------------'
+ *
+ * The purpose of having bra and ket is that we might i18n them as well.
+ */
+
+#define COLOR_FORMAT_STRING (WHITE" %s --%s"YELLOW"%s%s%s%s%s%s "GRAY)
+#define COLOR_FORMAT_STRING_BOOL (WHITE" %s --%s%s%s%s%s%s%s "GRAY)
+
+#define LINE_START 8
+#define PADDING_SPACES 25
+#ifdef WIN32
+# define OPTION_VALUE_SEP "="
+#else
+# define OPTION_VALUE_SEP " "
+#endif
+ vlc_list_t *p_list = NULL;
+ char psz_spaces_text[PADDING_SPACES+LINE_START+1];
+ char psz_spaces_longtext[LINE_START+3];
+ char psz_format[sizeof(COLOR_FORMAT_STRING)];
+ char psz_format_bool[sizeof(COLOR_FORMAT_STRING_BOOL)];
+ char psz_buffer[10000];
+ char psz_short[4];
+ int i_index;
+ int i_width = ConsoleWidth() - (PADDING_SPACES+LINE_START+1);
+ int i_width_description = i_width + PADDING_SPACES - 1;
+ bool b_advanced = config_GetInt( p_this, "advanced" ) > 0;
+ bool b_description = config_GetInt( p_this, "help-verbose" ) > 0;
+ bool b_description_hack;
+ bool b_color = config_GetInt( p_this, "color" ) > 0;
+ bool b_has_advanced = false;
+
+ memset( psz_spaces_text, ' ', PADDING_SPACES+LINE_START );
+ psz_spaces_text[PADDING_SPACES+LINE_START] = '\0';
+ memset( psz_spaces_longtext, ' ', LINE_START+2 );
+ psz_spaces_longtext[LINE_START+2] = '\0';
+#ifndef WIN32
+ if( !isatty( 1 ) )
+#endif
+ b_color = false; // don't put color control codes in a .txt file
+
+ if( b_color )
+ {
+ strcpy( psz_format, COLOR_FORMAT_STRING );
+ strcpy( psz_format_bool, COLOR_FORMAT_STRING_BOOL );
+ }
+ else
+ {
+ strcpy( psz_format, FORMAT_STRING );
+ strcpy( psz_format_bool, FORMAT_STRING );
+ }
+
+ /* List all modules */
+ p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE );
+
+ /* Ugly hack to make sure that the help options always come first
+ * (part 1) */
+ if( !psz_module_name )
+ Usage( p_this, "help" );
+
+ /* Enumerate the config for each module */
+ for( i_index = 0; i_index < p_list->i_count; i_index++ )
+ {
+ bool b_help_module;
+ module_t *p_parser = (module_t *)p_list->p_values[i_index].p_object;
+ module_config_t *p_item = NULL;
+ module_config_t *p_section = NULL;
+ module_config_t *p_end = p_parser->p_config + p_parser->confsize;
+
+ if( psz_module_name && strcmp( psz_module_name,
+ p_parser->psz_object_name ) )
+ {
+ char *const *pp_shortcut = p_parser->pp_shortcuts;
+ while( *pp_shortcut )
+ {
+ if( !strcmp( psz_module_name, *pp_shortcut ) )
+ break;
+ pp_shortcut ++;
+ }
+ if( !*pp_shortcut )
+ continue;
+ }
+
+ /* Ignore modules without config options */
+ if( !p_parser->i_config_items )
+ {
+ continue;
+ }
+
+ b_help_module = !strcmp( "help", p_parser->psz_object_name );
+ /* Ugly hack to make sure that the help options always come first
+ * (part 2) */
+ if( !psz_module_name && b_help_module )
+ continue;
+
+ /* Ignore modules with only advanced config options if requested */
+ if( !b_advanced )
+ {
+ for( p_item = p_parser->p_config;
+ p_item < p_end;
+ p_item++ )
+ {
+ if( (p_item->i_type & CONFIG_ITEM) &&
+ !p_item->b_advanced ) break;
+ }
+ }
+
+ /* Print name of module */
+ if( strcmp( "main", p_parser->psz_object_name ) )
+ {
+ if( b_color )
+ utf8_fprintf( stdout, "\n " GREEN "%s" GRAY "\n",
+ p_parser->psz_longname );
+ else
+ utf8_fprintf( stdout, "\n %s\n", p_parser->psz_longname );
+ }
+ if( p_parser->psz_help )
+ {
+ if( b_color )
+ utf8_fprintf( stdout, CYAN" %s\n"GRAY, p_parser->psz_help );
+ else
+ utf8_fprintf( stdout, " %s\n", p_parser->psz_help );
+ }
+
+ /* Print module options */
+ for( p_item = p_parser->p_config;
+ p_item < p_end;
+ p_item++ )
+ {
+ char *psz_text, *psz_spaces = psz_spaces_text;
+ const char *psz_bra = NULL, *psz_type = NULL, *psz_ket = NULL;
+ const char *psz_suf = "", *psz_prefix = NULL;
+ signed int i;
+ size_t i_cur_width;
+
+ /* Skip removed options */
+ if( p_item->b_removed )
+ {
+ continue;
+ }
+ /* Skip advanced options if requested */
+ if( p_item->b_advanced && !b_advanced )
+ {
+ b_has_advanced = true;
+ continue;
+ }
+
+ switch( p_item->i_type )
+ {
+ case CONFIG_HINT_CATEGORY:
+ case CONFIG_HINT_USAGE:
+ if( !strcmp( "main", p_parser->psz_object_name ) )
+ {
+ if( b_color )
+ utf8_fprintf( stdout, GREEN "\n %s\n" GRAY,
+ p_item->psz_text );
+ else
+ utf8_fprintf( stdout, "\n %s\n", p_item->psz_text );
+ }
+ if( b_description && p_item->psz_longtext )
+ {
+ if( b_color )
+ utf8_fprintf( stdout, CYAN " %s\n" GRAY,
+ p_item->psz_longtext );
+ else
+ utf8_fprintf( stdout, " %s\n", p_item->psz_longtext );
+ }
+ break;
+
+ case CONFIG_HINT_SUBCATEGORY:
+ if( strcmp( "main", p_parser->psz_object_name ) )
+ break;
+ case CONFIG_SECTION:
+ p_section = p_item;
+ break;
+
+ case CONFIG_ITEM_STRING:
+ case CONFIG_ITEM_FILE:
+ case CONFIG_ITEM_DIRECTORY:
+ case CONFIG_ITEM_MODULE: /* We could also have "=<" here */
+ case CONFIG_ITEM_MODULE_CAT:
+ case CONFIG_ITEM_MODULE_LIST:
+ case CONFIG_ITEM_MODULE_LIST_CAT:
+ case CONFIG_ITEM_FONT:
+ case CONFIG_ITEM_PASSWORD:
+ print_help_section( p_section, b_color, b_description );
+ p_section = NULL;
+ psz_bra = OPTION_VALUE_SEP "<";
+ psz_type = _("string");
+ psz_ket = ">";
+
+ if( p_item->ppsz_list )
+ {
+ psz_bra = OPTION_VALUE_SEP "{";
+ psz_type = psz_buffer;
+ psz_buffer[0] = '\0';
+ for( i = 0; p_item->ppsz_list[i]; i++ )
+ {
+ if( i ) strcat( psz_buffer, "," );
+ strcat( psz_buffer, p_item->ppsz_list[i] );
+ }
+ psz_ket = "}";
+ }
+ break;
+ case CONFIG_ITEM_INTEGER:
+ case CONFIG_ITEM_KEY: /* FIXME: do something a bit more clever */
+ print_help_section( p_section, b_color, b_description );
+ p_section = NULL;
+ psz_bra = OPTION_VALUE_SEP "<";
+ psz_type = _("integer");
+ psz_ket = ">";
+
+ if( p_item->min.i || p_item->max.i )
+ {
+ sprintf( psz_buffer, "%s [%i .. %i]", psz_type,
+ p_item->min.i, p_item->max.i );
+ psz_type = psz_buffer;
+ }
+
+ if( p_item->i_list )
+ {
+ psz_bra = OPTION_VALUE_SEP "{";
+ psz_type = psz_buffer;
+ psz_buffer[0] = '\0';
+ for( i = 0; p_item->ppsz_list_text[i]; i++ )
+ {
+ if( i ) strcat( psz_buffer, ", " );
+ sprintf( psz_buffer + strlen(psz_buffer), "%i (%s)",
+ p_item->pi_list[i],
+ p_item->ppsz_list_text[i] );
+ }
+ psz_ket = "}";
+ }
+ break;
+ case CONFIG_ITEM_FLOAT:
+ print_help_section( p_section, b_color, b_description );
+ p_section = NULL;
+ psz_bra = OPTION_VALUE_SEP "<";
+ psz_type = _("float");
+ psz_ket = ">";
+ if( p_item->min.f || p_item->max.f )
+ {
+ sprintf( psz_buffer, "%s [%f .. %f]", psz_type,
+ p_item->min.f, p_item->max.f );
+ psz_type = psz_buffer;
+ }
+ break;
+ case CONFIG_ITEM_BOOL:
+ print_help_section( p_section, b_color, b_description );
+ p_section = NULL;
+ psz_bra = ""; psz_type = ""; psz_ket = "";
+ if( !b_help_module )
+ {
+ psz_suf = p_item->value.i ? _(" (default enabled)") :
+ _(" (default disabled)");
+ }
+ break;
+ }
+
+ if( !psz_type )
+ {
+ continue;
+ }
+
+ /* Add short option if any */
+ if( p_item->i_short )
+ {
+ sprintf( psz_short, "-%c,", p_item->i_short );
+ }
+ else
+ {
+ strcpy( psz_short, " " );
+ }
+
+ i = PADDING_SPACES - strlen( p_item->psz_name )
+ - strlen( psz_bra ) - strlen( psz_type )
+ - strlen( psz_ket ) - 1;
+
+ if( p_item->i_type == CONFIG_ITEM_BOOL && !b_help_module )
+ {
+ psz_prefix = ", --no-";
+ i -= strlen( p_item->psz_name ) + strlen( psz_prefix );
+ }
+
+ if( i < 0 )
+ {
+ psz_spaces[0] = '\n';
+ i = 0;
+ }
+ else
+ {
+ psz_spaces[i] = '\0';
+ }
+
+ if( p_item->i_type == CONFIG_ITEM_BOOL && !b_help_module )
+ {
+ utf8_fprintf( stdout, psz_format_bool, psz_short,
+ p_item->psz_name, psz_prefix, p_item->psz_name,
+ psz_bra, psz_type, psz_ket, psz_spaces );
+ }
+ else
+ {
+ utf8_fprintf( stdout, psz_format, psz_short, p_item->psz_name,
+ "", "", psz_bra, psz_type, psz_ket, psz_spaces );
+ }
+
+ psz_spaces[i] = ' ';
+
+ /* We wrap the rest of the output */
+ sprintf( psz_buffer, "%s%s", p_item->psz_text, psz_suf );
+ b_description_hack = b_description;
+
+ description:
+ psz_text = psz_buffer;
+ i_cur_width = b_description && !b_description_hack
+ ? i_width_description
+ : i_width;
+ while( *psz_text )
+ {
+ char *psz_parser, *psz_word;
+ size_t i_end = strlen( psz_text );
+
+ /* If the remaining text fits in a line, print it. */
+ if( i_end <= i_cur_width )
+ {
+ if( b_color )
+ {
+ if( !b_description || b_description_hack )
+ utf8_fprintf( stdout, BLUE"%s\n"GRAY, psz_text );
+ else
+ utf8_fprintf( stdout, "%s\n", psz_text );
+ }
+ else
+ {
+ utf8_fprintf( stdout, "%s\n", psz_text );
+ }
+ break;
+ }
+
+ /* Otherwise, eat as many words as possible */
+ psz_parser = psz_text;
+ do
+ {
+ psz_word = psz_parser;
+ psz_parser = strchr( psz_word, ' ' );
+ /* If no space was found, we reached the end of the text
+ * block; otherwise, we skip the space we just found. */
+ psz_parser = psz_parser ? psz_parser + 1
+ : psz_text + i_end;
+
+ } while( (size_t)(psz_parser - psz_text) <= i_cur_width );
+
+ /* We cut a word in one of these cases:
+ * - it's the only word in the line and it's too long.
+ * - we used less than 80% of the width and the word we are
+ * going to wrap is longer than 40% of the width, and even
+ * if the word would have fit in the next line. */
+ if( psz_word == psz_text
+ || ( (size_t)(psz_word - psz_text) < 80 * i_cur_width / 100
+ && (size_t)(psz_parser - psz_word) > 40 * i_cur_width / 100 ) )
+ {
+ char c = psz_text[i_cur_width];
+ psz_text[i_cur_width] = '\0';
+ if( b_color )
+ {
+ if( !b_description || b_description_hack )
+ utf8_fprintf( stdout, BLUE"%s\n%s"GRAY,
+ psz_text, psz_spaces );
+ else
+ utf8_fprintf( stdout, "%s\n%s",
+ psz_text, psz_spaces );
+ }
+ else
+ {
+ utf8_fprintf( stdout, "%s\n%s", psz_text, psz_spaces );
+ }
+ psz_text += i_cur_width;
+ psz_text[0] = c;
+ }
+ else
+ {
+ psz_word[-1] = '\0';
+ if( b_color )
+ {
+ if( !b_description || b_description_hack )
+ utf8_fprintf( stdout, BLUE"%s\n%s"GRAY,
+ psz_text, psz_spaces );
+ else
+ utf8_fprintf( stdout, "%s\n%s",
+ psz_text, psz_spaces );
+ }
+ else
+ {
+ utf8_fprintf( stdout, "%s\n%s", psz_text, psz_spaces );
+ }
+ psz_text = psz_word;
+ }
+ }
+
+ if( b_description_hack && p_item->psz_longtext )
+ {
+ sprintf( psz_buffer, "%s%s", p_item->psz_longtext, psz_suf );
+ b_description_hack = false;
+ psz_spaces = psz_spaces_longtext;
+ utf8_fprintf( stdout, "%s", psz_spaces );
+ goto description;
+ }
+ }
+ }
+
+ if( b_has_advanced )
+ {
+ if( b_color )
+ utf8_fprintf( stdout, "\n" WHITE "%s" GRAY " %s\n", _( "Note:" ),
+ _( "add --advanced to your command line to see advanced options."));
+ else
+ utf8_fprintf( stdout, "\n %s %s\n", _( "Note:" ),
+ _( "add --advanced to your command line to see advanced options."));
+ }
+
+ /* Release the module list */
+ vlc_list_release( p_list );