[vlc-commits] [Git][videolan/vlc][master] 26 commits: getopt: remove stray form-feed (0xC) char

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sat Dec 18 13:59:38 UTC 2021



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
34fe1c70 by Lyndon Brown at 2021-12-18T12:32:29+00:00
getopt: remove stray form-feed (0xC) char

traces back to 2001
cfbe86907a89f937b79462cb2876ef7e3cfd4500

- - - - -
6c9127b7 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: simplify short option array initialisation

- - - - -
6b7eee26 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: use correct name for neg-bool options in depr warn

where negative variants of deprecated boolean options are used, the
warning would previously specify the positive variant instead, which is
silly and potentially confusing to a user.

- - - - -
3cf3bb4c by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: add error label to error message

clarifies that this is an error message; helps users find errors by
searching output for "error" in some situations maybe?; better
consistency; kinda expected; and combined with later work to add
colour highlighting, improves UX by helping to highlight problems
in terminal output.

- - - - -
0575b068 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: drop 'vlc:' from error text

unnecessary and ugly for the vlc media player use case, hopefully
also unnecessary for other libvlc use cases.

note that for now at least, this error output is followed
immediately with another line that includes "vlc" in it.

- - - - -
77282fb8 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: capitalise error messages

- - - - -
f104f95e by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: clarify missing option value error

for options that need an accompanying "value", users can either supply it
within the same command-line argument as the option itself, like
`--foo=bar`, or within the next argument, like `--foo bar`. (for short
options this would be `-fbar` and `-f bar` respectively).

it can be argued that it is possibly confusing to users to thus speak of
a missing "argument" when such an option "value" is missing. let's speak
of a missing option "value" instead.

- - - - -
251f67ec by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: reject short option chars with special getopt meaning

getopt can return ':' and '?' chars to indicate special conditions. there
is nothing to prevent such characters being assigned to options, and thus
there is a risk of misinterpretation, for instance misinterpreting a sign
of a missing option value error condition as a match for such a short
option. this change ensures that in the hypothetical event someone is
stupid enough to try to assign one of these chars to an option, we prevent
situations of misinterpretation of error conditions, at the acceptable
expense of that short option not being allowed to work at all.

- - - - -
5ba65ea2 by Lyndon Brown at 2021-12-18T12:32:29+00:00
plugins: reject a few obvious invalid short option char choices

null = obvious
':' and '?' have special getopt meaning

- - - - -
58244c94 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: distinuish between 'unknown option' and 'missing option value' issues

- - - - -
4417e1fb by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: reorganise error handling

...to prepare for text translation and colour labels.

(i am not certain that tacking the option onto the end, as we were,
will be suitable for all translations).

i've avoided using allocation to construct the option name portion of the
output, since i expect that would not be liked, and i'm not convinced about
it myself. it can always be reconsidered later however.

- - - - -
0cacb2e6 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: translate errors and warnings

- - - - -
863a19ed by Lyndon Brown at 2021-12-18T12:32:29+00:00
config: move some console stuff to new internal header

the terminal control code stuff is relevant to (and wanted by me for)
cmdline.c

- - - - -
077aa681 by Lyndon Brown at 2021-12-18T12:32:29+00:00
config: tweak terminal sequence set

 - replaced use of `GRAY` and `WHITE` for resetting with `RESET` and
   `RESET_BOLD` respectively.
 - addressed hidden use of bold by creating separate colour and colour+bold
   defines.
 - added a `TS_` prefix to help clarify that the defines, where they are
   used, are terminal sequences.

- - - - -
6900989f by Lyndon Brown at 2021-12-18T12:32:29+00:00
help: fix unnecessary use of `TS_RESET_BOLD`

- - - - -
e4fa5c37 by Lyndon Brown at 2021-12-18T12:32:29+00:00
config: remove duplicate include

- - - - -
e77ae919 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: use colour error/warn labels

improves UX by helping to highlight problems in terminal output.

for now, as with the rest of the codebase, we rely upon only a
simple `isatty()` check for automatic colouring decisions, which
does not work for Windows. implementing `isatty()` for Windows to
could perhaps be tackled later.

---

note that user control over use of colour in terminal output via the
use of `--color`/`--no-color` options is deliberately ignored with
respect to the use of colour within error and warning output in
response to any problems encountered whilst processing the supplied
command-line arguments themselves (unknown option error; missing option
value error; obsolete option warning), having decided that this was the
best choice. this was questioned in review of this work, so i have
expanded/documented my thoughts on it here below.

 1. default behaviour depends upon whether or not a detection routine
    determines that output is connected to a tty. if so then we choose
    to use colour in the hope that most users will appreciate the
    highlighting of problems. if not, i.e. output is thus being
    redirected into a log file or another application, we choose to
    not use colour because embeddeding terminal sequences into the
    output in such situations risks causing confusion due to the
    likely possibility of them not being consumed as intended.

    the only situations where users may want or need to change this
    default behaviour, are when either:

     a. they happen to dislike the use of colour.
     b. they want to force use of colour on Windows, due to current
        lack of a tty detection mechanism.

    the latter of course becomes redundant as soon as we implement
    an `isatty()` for Windows.

    it is reasonable in my opinion to fail to honour such a user
    preference for the limited circumstances of encountering a problem
    with the very data-set in which the user communicates that
    preference.

    never-the-less, let's briefly explore the possible solutions
    that could in theory be implemented to try to honour such user
    preference for such output.

 2. solution 1: "pre-scanning"

    the concept was raised of "pre-scanning" the set of arguments to
    try to learn the user's indicated preference ahead of properly
    processing them, such that we can ensure that it is obeyed if/when
    outputting such an error/warning for a problem encountered in
    undertaking such processing. i.e. essentially walking through the
    argument list looking out for arguments matching a few fixed
    strings like "--color", and thus taking note of the preference
    before we begin the proper argument processing work.

    it should be understood that the rules of proper argument processing
    dictate that you cannot with certainty know the nature of an
    argument until you have processed fully all of those preceeding it.
    options that take data values can be used with the value given
    within the same argument or supplied within the next one, and so you
    never know if an arbitrarily given argument must be consumed as a
    data value rather than treated as a possible option until you've
    processed all proceeding arguments; a special "early terminator"
    (`--`) argument indicates that all subsequent arguments must be
    treated as generic arguments (that must not be interpretted as
    options); and an unknown option argument brings processing to a halt
    since it is not possible to correctly interpret the nature of
    anything subsequently given.

    despite this, practically speaking we could get away with
    pre-scanning - there are no "short" option forms of these toggle
    options to complicate things; and making simple assumptions that
    arguments matching "--color" or "--no-color" (or "--nocolor") are
    actual uses of those options is pretty reasonable along with assuming
    that a "--" argument is an actual early terminator, since such
    assumptions are almost always likely to be correct except in certain
    very unusual circumstances of broken CLI use or from users
    deliberately looking for flaws.

    however, i do not feel that it is worth adding a chunk of code
    just to essentially ensure that a hypothetical and presumably rare
    CLI user who dislikes colour will not see it should they happen to
    make a mistake in their CLI usage.

    (it might be noted that the vlc binaries do similarly flawed scans
    to steal one or two options, but that's not readily avoidable).

 3. solution 2: "updating as we go":

    as an alternative concept, we could, upon successfully processing
    each option, check if the option is `--color` or `--no-color` and if
    so, update the `color` boolean accordingly, such that if we run into
    a problem and thus need to output an optionally coloured error or
    warning label, we might thus obey the user's preference when doing so.

    however, we have equal chance that we do the opposite since there
    could be a later argument that we did not yet get to that flips the
    preference the other way (e.g. `vlc --no-color --foo --color`). in
    a worst case scenario the user might end up with a mix of coloured
    and non-coloured output that's messy and possibly confusing.

    again, i do not feel that it is worth implementing code to do this,
    to introduce an inefficiency into the option processing for such
    little benefit, especially with the risk of this creating a mess
    of inconsistency in some cases.

we (i) should endeavour to introduce `isatty()` support for Windows such
that Windows CLI users get the benefits of colour highlighting, but i
feel that it is perfectly reasonable to take a stance of not bothering to
try to enforce user no-color preference upon argument processing
errors/warnings.

- - - - -
8fb6f8d0 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: track obsolete state in `vlc_option`

for subsequent use with suggestion matching.

- - - - -
86f9e53b by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: add jaro-winkler string measuring implementation

to be used with suggestion matching.

this implementation is based upon the implementation from the `strsim` Rust
crate, authored by Danny Guo, available at [1]; more specifically the (as
yet un-merged) optimised copy authored by myself, available at [2]. the
code is available under the MIT license.

one implementation difference is that we use floats rather than doubles
since we only intend to use this for suggestion matching in unknown option
error messages and we don't need the extra precision.

[1]: https://github.com/dguo/strsim-rs
[2]: https://github.com/dguo/strsim-rs/pull/31

- - - - -
4ad4a388 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: add unknown long option suggestion matching

i.e. if a user tries a long option which does not exist, if it is a close
enough match to one or more available options, the error message can
include the best match as a helpful suggestion.

- - - - -
8fdcd829 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: use cleaner 'see --help' text

old: "Try `vlc --help' for more information."
new: "For more information try `--help`"

in experiments, the new text proved much cleaner. it becomes yet more
clean, having dropped "vlc" from `vlc --help`, when we then drop the
quotes around the remaining `--help` in the commit after next.

note, we now no longer have "vlc" in any of the error output. this
is not a problem for the vlc app itself obviously; i hope it is not
a problem for any other libvlc use cases.

- - - - -
3df5e077 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: colour highlight option names in error text

inspired by Rust's cargo output, IMO this adds a nice little
enhancement to UX along with the colour error/warn labels.

- - - - -
6319b067 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: drop quotes for valid option names

experimenting with this i decided that it is cleaner to not use them, for
valid option names at least. they are still used for unknown option quoting
for instance, and where we are quoting multiple CLI arguments like
`--foo <VAL>` or `vlc --foo`.

- - - - -
4e691ef9 by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: simplify error handling code

creating the option label with `asprintf()` up front allows us to tidy
things up quite a bit, removing most notably the duplication of strings
that differ only between char and string format items.

- - - - -
5802f57c by Lyndon Brown at 2021-12-18T12:32:29+00:00
cmdline: add missing float case

i don't believe we have any short options assigned to float options
currently, but we should have this case here prepared to handle it.

- - - - -
dbf81e9a by Lyndon Brown at 2021-12-18T12:32:29+00:00
help: add explicit entries for skipped cases

to be clear about what we do for them.

- - - - -


10 changed files:

- src/Makefile.am
- + src/config/ansi_term.h
- src/config/cmdline.c
- src/config/getopt.c
- src/config/help.c
- + src/config/jaro_winkler.c
- src/config/vlc_getopt.h
- + src/config/vlc_jaro_winkler.h
- src/modules/entry.c
- + src/test/jaro_winkler.c


Changes:

=====================================
src/Makefile.am
=====================================
@@ -203,6 +203,7 @@ libvlccore_la_SOURCES = \
 	missing.c \
 	revision.c \
 	version.c \
+	config/ansi_term.h \
 	config/configuration.h \
 	config/core.c \
 	config/chain.c \
@@ -212,6 +213,8 @@ libvlccore_la_SOURCES = \
 	config/cmdline.c \
 	config/getopt.c \
 	config/vlc_getopt.h \
+	config/jaro_winkler.c \
+	config/vlc_jaro_winkler.h \
 	extras/libc.c \
 	media_source/media_source.c \
 	media_source/media_source.h \
@@ -595,6 +598,7 @@ check_PROGRAMS = \
 	test_executor \
 	test_i18n_atof \
 	test_interrupt \
+	test_jaro_winkler \
 	test_list \
 	test_md5 \
 	test_picture_pool \
@@ -625,6 +629,7 @@ test_executor_SOURCES = test/executor.c
 test_i18n_atof_SOURCES = test/i18n_atof.c
 test_interrupt_SOURCES = test/interrupt.c
 test_interrupt_LDADD = $(LDADD) $(LIBS_libvlccore)
+test_jaro_winkler_SOURCES = test/jaro_winkler.c config/jaro_winkler.c
 test_list_SOURCES = test/list.c
 test_md5_SOURCES = test/md5.c
 test_picture_pool_SOURCES = test/picture_pool.c


=====================================
src/config/ansi_term.h
=====================================
@@ -0,0 +1,122 @@
+/*****************************************************************************
+ * ansi_term.h: Common declarations and helpers for ANSI terminal handling
+ *****************************************************************************
+ * Copyright (C) 1998-2011 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_ANSI_TERM_H
+#define VLC_ANSI_TERM_H 1
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if !defined( _WIN32 )
+# include <termios.h>
+# include <sys/ioctl.h>
+#endif
+
+/* ANSI terminal control ("escape") sequences */
+
+/* Terminal control sequence construction */
+#define term_seq(x) "\033[" #x "m"
+
+/**
+ * Codes:
+ *
+ * Effects:
+ *   - Normal:    0 (reset)
+ *   - Bold:      1
+ *   - Dim:       2
+ *   - Italic:    3
+ *   - Underline: 4
+ *   - Reverse:   7
+ *   - Invisible: 8
+ *   - Strike:    9 (Strike-through)
+ *
+ * Color set 1:
+ *   - Black:   30
+ *   - Red      31
+ *   - Green    32
+ *   - Yellow:  33
+ *   - Blue:    34
+ *   - Magenta: 35
+ *   - Cyan:    36
+ *   - White:   37
+ *
+ * Color set 2:
+ *   - Black:   90
+ *   - Red:     91
+ *   - Green:   92
+ *   - Yellow:  93
+ *   - Blue:    94
+ *   - Magenta: 95
+ *   - Cyan:    96
+ *   - White:   97
+ *
+ * Text background color highlighting, set 1:
+ *   - Black:   40
+ *   - Red:     41
+ *   - Green:   42
+ *   - Yellow:  43
+ *   - Blue:    44
+ *   - Magenta: 45
+ *   - Cyan:    46
+ *   - White:   47
+ *
+ * Text background color highlighting, set 2:
+ *   - Black:   100
+ *   - Red:     101
+ *   - Green:   102
+ *   - Yellow:  103
+ *   - Blue:    104
+ *   - Magenta: 105
+ *   - Cyan:    106
+ *   - White:   107
+ */
+
+#define TS_RESET term_seq(0)
+
+#define TS_RESET_BOLD term_seq(0;1)
+
+#define TS_BOLD       term_seq(1)
+#define TS_DIM        term_seq(2)
+#define TS_ITALIC     term_seq(3)
+#define TS_UNDERSCORE term_seq(4)
+#define TS_REVERSE    term_seq(7)
+#define TS_INVISIBLE  term_seq(8)
+#define TS_STRIKE     term_seq(9)
+
+#define TS_BLACK   term_seq(30)
+#define TS_RED     term_seq(31)
+#define TS_GREEN   term_seq(32)
+#define TS_YELLOW  term_seq(33)
+#define TS_BLUE    term_seq(34)
+#define TS_MAGENTA term_seq(35)
+#define TS_CYAN    term_seq(36)
+#define TS_WHITE   term_seq(37)
+
+#define TS_BLACK_BOLD   term_seq(30;1)
+#define TS_RED_BOLD     term_seq(31;1)
+#define TS_GREEN_BOLD   term_seq(32;1)
+#define TS_YELLOW_BOLD  term_seq(33;1)
+#define TS_BLUE_BOLD    term_seq(34;1)
+#define TS_MAGENTA_BOLD term_seq(35;1)
+#define TS_CYAN_BOLD    term_seq(36;1)
+#define TS_WHITE_BOLD   term_seq(37;1)
+
+#endif


=====================================
src/config/cmdline.c
=====================================
@@ -32,7 +32,9 @@
 #include <vlc_plugin.h>
 
 #include "vlc_getopt.h"
+#include "vlc_jaro_winkler.h"
 
+#include "ansi_term.h"
 #include "configuration.h"
 #include "modules/modules.h"
 
@@ -63,7 +65,8 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
 #define b_ignore_errors (pindex == NULL)
 
     /* Short options */
-    const module_config_t *pp_shortopts[256];
+    i_shortopts = 0;
+    const module_config_t *pp_shortopts[256] = { NULL };
     char *psz_shortopts;
 
     /*
@@ -104,11 +107,10 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
         ppsz_argv = argv_copy;
     }
 
-    i_shortopts = 0;
-    for( i_index = 0; i_index < 256; i_index++ )
-    {
-        pp_shortopts[i_index] = NULL;
-    }
+    /* Indicate that we want to know the difference between unknown option and
+       missing option value issues */
+    psz_shortopts[0] = ':';
+    i_shortopts = 1;
 
     /* Fill the p_longopts and psz_shortopts structures */
     i_index = 0;
@@ -128,6 +130,7 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
             if( p_longopts[i_index].name == NULL ) continue;
             p_longopts[i_index].flag = &flag;
             p_longopts[i_index].val = 0;
+            p_longopts[i_index].is_obsolete = param->obsolete;
 
             if( CONFIG_CLASS(p_item->i_type) != CONFIG_ITEM_BOOL )
                 p_longopts[i_index].has_arg = true;
@@ -143,6 +146,7 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
                     continue;
                 p_longopts[i_index].name = psz_name;
                 p_longopts[i_index].has_arg = false;
+                p_longopts[i_index].is_obsolete = param->obsolete;
                 p_longopts[i_index].flag = &flag;
                 p_longopts[i_index].val = 1;
                 i_index++;
@@ -151,6 +155,7 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
                     continue;
                 p_longopts[i_index].name = psz_name;
                 p_longopts[i_index].has_arg = false;
+                p_longopts[i_index].is_obsolete = param->obsolete;
                 p_longopts[i_index].flag = &flag;
                 p_longopts[i_index].val = 1;
             }
@@ -177,6 +182,10 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
     psz_shortopts[i_shortopts] = '\0';
 
     int ret = -1;
+    bool color = false;
+#ifndef _WIN32
+    color = (isatty(STDERR_FILENO));
+#endif
 
     /*
      * Parse the command line options
@@ -191,10 +200,11 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
         if( i_cmd == 0 )
         {
             module_config_t *p_conf;
-            const char *psz_name = p_longopts[i_index].name;
+            const char *psz_full_name = p_longopts[i_index].name;
+            const char *psz_name = psz_full_name;
 
             /* Check if we deal with a --nofoo or --no-foo long option */
-            if( flag ) psz_name += psz_name[2] == '-' ? 3 : 2;
+            if( flag ) psz_name += psz_full_name[2] == '-' ? 3 : 2;
 
             /* Store the configuration option */
             p_conf = config_FindConfig( psz_name );
@@ -206,8 +216,10 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
                 if (param->obsolete)
                 {
                     fprintf(stderr,
-                            "Warning: option --%s no longer exists.\n",
-                            psz_name);
+                            _( "%sWarning:%s Option --%s no longer exists.\n" ),
+                            color ? TS_YELLOW_BOLD : "",
+                            color ? TS_RESET : "",
+                            psz_full_name);
                     continue;
                 }
 
@@ -242,7 +254,7 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
         }
 
         /* A short option has been recognized */
-        if( pp_shortopts[i_cmd] != NULL )
+        if( i_cmd != '?' && i_cmd != ':' && pp_shortopts[i_cmd] != NULL )
         {
             const char *name = pp_shortopts[i_cmd]->psz_name;
             switch( CONFIG_CLASS(pp_shortopts[i_cmd]->i_type) )
@@ -264,6 +276,10 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
                                         strtoll(state.arg, NULL, 0) );
                     }
                     break;
+                case CONFIG_ITEM_FLOAT:
+                    var_Create( p_this, name, VLC_VAR_FLOAT );
+                    var_SetFloat( p_this, name, us_atof(state.arg) );
+                    break;
                 case CONFIG_ITEM_BOOL:
                     var_Create( p_this, name, VLC_VAR_BOOL );
                     var_SetBool( p_this, name, true );
@@ -273,20 +289,54 @@ int config_LoadCmdLine( vlc_object_t *p_this, int i_argc,
             continue;
         }
 
-        /* Internal error: unknown option */
+        /* Internal error: unknown option or missing option value */
         if( !b_ignore_errors )
         {
-            fputs( "vlc: unknown option"
-                     " or missing mandatory argument ", stderr );
-            if( state.opt )
+            char *optlabel;
+            if ( (state.opt && asprintf(&optlabel, "%s-%c%s",
+                                        color ? TS_YELLOW : "", state.opt,
+                                        color ? TS_RESET : "") < 0)
+              || (!state.opt && asprintf(&optlabel, "%s%s%s",
+                                         color ? TS_YELLOW : "", ppsz_argv[state.ind-1],
+                                         color ? TS_RESET : "") < 0) )
             {
-                fprintf( stderr, "`-%c'\n", state.opt );
+                /* just ignore failure - unlikely and not worth trying to handle in some way */
+                optlabel = NULL;
+            }
+
+            fprintf( stderr, _( "%sError:%s " ), color ? TS_RED_BOLD : "", color ? TS_RESET : "");
+            if (i_cmd == ':')
+            {
+                fprintf( stderr, _( "Missing mandatory value for option %s\n" ), optlabel );
             }
             else
             {
-                fprintf( stderr, "`%s'\n", ppsz_argv[state.ind-1] );
+                fprintf( stderr, _( "Unknown option `%s'\n" ), optlabel );
+
+                /* suggestion matching */
+                if( !state.opt )
+                {
+                    float jw_filter = 0.8, best_metric = jw_filter, metric;
+                    const char *best = NULL;
+                    const char *jw_a = ppsz_argv[state.ind-1] + 2;
+                    for (size_t i = 0; i < (size_t)i_opts; i++) {
+                        if (p_longopts[i].is_obsolete)
+                            continue;
+                        const char *jw_b = p_longopts[i].name;
+                        if (vlc_jaro_winkler(jw_a, jw_b, &metric) == 0) { //ignore failed malloc calls
+                            if (metric > best_metric || (!best && metric >= jw_filter)) {
+                                best = jw_b;
+                                best_metric = metric;
+                            }
+                        }
+                    }
+                    if (best)
+                        fprintf( stderr, _( "       Did you mean %s--%s%s?\n" ),
+                                 color ? TS_GREEN : "", best, color ? TS_RESET : "" );
+                }
             }
-            fputs( "Try `vlc --help' for more information.\n", stderr );
+            fprintf( stderr, _( "For more information try %s--help%s\n" ),
+                     color ? TS_GREEN : "", color ? TS_RESET : "" );
             goto out;
         }
     }


=====================================
src/config/getopt.c
=====================================
@@ -92,7 +92,6 @@ static void exchange(char **argv, vlc_getopt_t *restrict state)
     state->last_nonopt = state->ind;
 }
 
-
 /* Scan elements of ARGV (whose length is ARGC) for option characters
    given in OPTSTRING.
 


=====================================
src/config/help.c
=====================================
@@ -35,17 +35,15 @@
 #include <vlc_modules.h>
 #include <vlc_plugin.h>
 #include <vlc_charset.h>
+#include "ansi_term.h"
 #include "modules/modules.h"
 #include "config/configuration.h"
 #include "libvlc.h"
 
 #if defined( _WIN32 )
-# include <vlc_charset.h>
 # define wcwidth(cp) ((void)(cp), 1) /* LOL */
 #else
 # include <unistd.h>
-# include <termios.h>
-# include <sys/ioctl.h>
 #endif
 
 #if defined( _WIN32 ) && !defined( VLC_WINSTORE_APP )
@@ -231,15 +229,6 @@ static void Help (vlc_object_t *p_this, char const *psz_help_name)
  *****************************************************************************
  * Print a short inline help. Message interface is initialized at this stage.
  *****************************************************************************/
-#   define COL(x)  "\033[" #x ";1m"
-#   define RED     COL(31)
-#   define GREEN   COL(32)
-#   define YELLOW  COL(33)
-#   define BLUE    COL(34)
-#   define MAGENTA COL(35)
-#   define CYAN    COL(36)
-#   define WHITE   COL(0)
-#   define GRAY    "\033[0m"
 #   define LINE_START      8
 #   define PADDING_SPACES 25
 
@@ -252,10 +241,10 @@ static void print_section(const module_t *m, const module_config_t **sect,
         return;
     *sect = NULL;
 
-    printf(color ? RED"   %s:\n"GRAY : "   %s:\n",
+    printf(color ? TS_RED_BOLD "   %s:\n" TS_RESET : "   %s:\n",
            module_gettext(m, item->psz_text));
     if (desc && item->psz_longtext != NULL)
-        printf(color ? MAGENTA"   %s\n"GRAY : "   %s\n",
+        printf(color ? TS_MAGENTA_BOLD "   %s\n" TS_RESET : "   %s\n",
                module_gettext(m, item->psz_longtext));
 }
 
@@ -264,7 +253,7 @@ static void print_desc(const char *str, unsigned margin, bool color)
     unsigned width = ConsoleWidth() - margin;
 
     if (color)
-        fputs(BLUE, stdout);
+        fputs(TS_BLUE_BOLD, stdout);
 
     const char *word = str;
     int wordlen = 0, wordwidth = 0;
@@ -321,7 +310,7 @@ static void print_desc(const char *str, unsigned margin, bool color)
 
     if (!newline)
         putchar(' ');
-    printf(color ? "%s\n"GRAY : "%s\n", word);
+    printf(color ? "%s\n" TS_RESET : "%s\n", word);
 }
 
 static int vlc_swidth(const char *str)
@@ -363,14 +352,19 @@ static void print_item(const module_t *m, const struct vlc_param *param,
             switch (item->i_type)
             {
                 case CONFIG_HINT_CATEGORY:
-                    printf(color ? GREEN "\n %s\n" GRAY : "\n %s\n",
+                    printf(color ? TS_GREEN_BOLD "\n %s\n" TS_RESET : "\n %s\n",
                            module_gettext(m, item->psz_text));
 
                     if (desc && item->psz_longtext != NULL)
-                        printf(color ? CYAN " %s\n" GRAY : " %s\n",
+                        printf(color ? TS_CYAN_BOLD " %s\n" TS_RESET : " %s\n",
                                module_gettext(m, item->psz_longtext));
                     break;
 
+                case CONFIG_CATEGORY:
+                case CONFIG_SUBCATEGORY:
+                    /* We ignore these here, using 'hints' instead */
+                    break;
+
                 case CONFIG_SECTION:
                     *section = item;
                     break;
@@ -503,11 +497,11 @@ static void print_item(const module_t *m, const struct vlc_param *param,
         strcpy(shortopt, "   ");
 
     if (CONFIG_CLASS(item->i_type) == CONFIG_ITEM_BOOL)
-        printf(color ? WHITE"  %s --%s"      "%s%s%s%s%s "GRAY
+        printf(color ? TS_BOLD "  %s --%s"      "%s%s%s%s%s " TS_RESET
                      : "  %s --%s%s%s%s%s%s ", shortopt, item->psz_name,
                prefix, item->psz_name, bra, type, ket);
     else
-        printf(color ? WHITE"  %s --%s"YELLOW"%s%s%s%s%s "GRAY
+        printf(color ? TS_BOLD "  %s --%s" TS_YELLOW_BOLD "%s%s%s%s%s " TS_RESET
                      : "  %s --%s%s%s%s%s%s ", shortopt, item->psz_name,
                "", "",  /* XXX */      bra, type, ket);
 
@@ -615,10 +609,10 @@ static void Usage (vlc_object_t *p_this, char const *psz_search)
             continue;
 
         /* Print name of module */
-        printf(color ? "\n " GREEN "%s" GRAY " (%s)\n" : "\n %s (%s)\n",
+        printf(color ? "\n " TS_GREEN_BOLD "%s" TS_RESET " (%s)\n" : "\n %s (%s)\n",
                module_gettext(m, m->psz_longname), objname);
         if (m->psz_help != NULL)
-            printf(color ? CYAN" %s\n"GRAY : " %s\n",
+            printf(color ? TS_CYAN_BOLD" %s\n" TS_RESET : " %s\n",
                    module_gettext(m, m->psz_help));
 
         if (psz_search != NULL && p->conf.count == 0)
@@ -637,7 +631,7 @@ static void Usage (vlc_object_t *p_this, char const *psz_search)
     }
 
     if (!found)
-        printf(color ? "\n" WHITE "%s" GRAY "\n" : "\n%s\n",
+        printf(color ? "\n" TS_BOLD "%s" TS_RESET "\n" : "\n%s\n",
                _("No matching module found. Use --list or "
                  "--list-verbose to list available modules."));
 }
@@ -669,7 +663,7 @@ static void ListModules (vlc_object_t *p_this, bool b_verbose)
     {
         module_t *p_parser = list[j];
         const char *objname = module_get_object (p_parser);
-        printf(color ? GREEN"  %-22s "WHITE"%s\n"GRAY : "  %-22s %s\n",
+        printf(color ? TS_GREEN_BOLD "  %-22s " TS_RESET_BOLD "%s\n" TS_RESET : "  %-22s %s\n",
                objname, module_gettext(p_parser, p_parser->psz_longname));
 
         if( b_verbose )
@@ -677,10 +671,10 @@ static void ListModules (vlc_object_t *p_this, bool b_verbose)
             const char *const *pp_shortcuts = p_parser->pp_shortcuts;
             for( unsigned i = 0; i < p_parser->i_shortcuts; i++ )
                 if( strcmp( pp_shortcuts[i], objname ) )
-                    printf(color ? CYAN"   s %s\n"GRAY : "   s %s\n",
+                    printf(color ? TS_CYAN_BOLD "   s %s\n" TS_RESET : "   s %s\n",
                            pp_shortcuts[i]);
             if (p_parser->psz_capability != NULL)
-                printf(color ? MAGENTA"   c %s (%d)\n"GRAY : "   c %s (%d)\n",
+                printf(color ? TS_MAGENTA_BOLD "   c %s (%d)\n" TS_RESET : "   c %s (%d)\n",
                        p_parser->psz_capability, p_parser->i_score);
         }
     }


=====================================
src/config/jaro_winkler.c
=====================================
@@ -0,0 +1,167 @@
+/*****************************************************************************
+ * jaro_winkler.c: jaro winkler string similarity algorithm implementation
+ *****************************************************************************
+ * Copyright 2015 Danny Guo
+ * Copyright 2018 Lyndon Brown
+ *
+ * Authors: Danny Guo <dguo at users.noreply.github.com>
+ *          Lyndon Brown <jnqnfe at gmail.com>
+ *
+ * Licensed under the MIT license. You may not copy, modify, or distribute this
+ * file except in compliance with said license. You can find a copy of this
+ * license either in the LICENSE file, or alternatively at
+ * <http://opensource.org/licenses/MIT>.
+ *****************************************************************************
+ * This file is based upon the Jaro Winkler implementation of the `strsim`
+ * Rust crate, authored by Danny Guo, available at
+ * <https://github.com/dguo/strsim-rs>; more specifically the (as yet un-merged)
+ * optimised copy authored by myself (Lyndon Brown), available at
+ * <https://github.com/dguo/strsim-rs/pull/31>. The code is available under the
+ * MIT license.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+
+#include "vlc_jaro_winkler.h"
+
+#define MAX(a, b) ( ((a) > (b)) ? (a) : (b) )
+#define MIN(a, b) ( ((a) < (b)) ? (a) : (b) )
+
+/**
+ * Checks both strings for a common prefix, returning the number of matching
+ * bytes.
+ */
+static inline size_t split_on_common_prefix(const char *a, const char *b) {
+    size_t len = 0;
+    while (*(a) && *(b) && *(a++) == *(b++)) len++;
+    return len;
+}
+
+/**
+ * This is the inner Jaro algorithm, with a parameter for passing back the
+ * length of the prefix common to both strings, used for efficiency of the
+ * Jaro-Winkler implementation.
+ */
+static inline int jaro_inner(const char *a, const char *b, size_t *ret_prefix_cc, float* res) {
+    assert(a && b && ret_prefix_cc && res);
+
+    if ((a[0] == '\0') ^ (b[0] == '\0')) {
+        *res = 0.0;
+        return 0;
+    }
+
+    size_t prefix_char_count = split_on_common_prefix(a, b);
+    const char *a_suffix = a + prefix_char_count;
+    const char *b_suffix = b + prefix_char_count;
+
+    if (a_suffix[0] == '\0' && b_suffix[0] == '\0') {
+        *res = 1.0;
+        return 0;
+    }
+
+    *ret_prefix_cc = prefix_char_count;
+
+    size_t a_numchars = strlen(a_suffix) + prefix_char_count;
+    size_t b_numchars = strlen(b_suffix) + prefix_char_count;
+
+    // The check for lengths of one here is to prevent integer overflow when
+    // calculating the search range.
+    if (a_numchars == 1 && b_numchars == 1) {
+        *res = 0.0;
+        return 0;
+    }
+
+    size_t search_range = (MAX(a_numchars, b_numchars) / 2) - 1;
+
+    /* catch overflow */
+    assert(a_numchars <= INT_MAX);
+    assert(search_range <= INT_MAX);
+
+    bool *b_consumed = calloc(b_numchars, sizeof(*b_consumed));
+    if (!b_consumed) {
+        *res = 0.0;
+        return -1;
+    }
+
+    size_t matches = prefix_char_count;
+    size_t transpositions = 0;
+    size_t b_match_index = 0;
+
+    const char *a_char = a_suffix;
+    for (size_t i = 0; *a_char; i++) {
+        ssize_t tmp = (ssize_t)i - (ssize_t)search_range;
+        size_t bound_start = (tmp >= 0) ? tmp : 0;
+        size_t bound_end = MIN(b_numchars, i + search_range + 1);
+
+        if (bound_start >= bound_end) {
+            a_char++;
+            continue;
+        }
+
+        const char *b_char = b_suffix + bound_start;
+        for (size_t j = bound_start; *b_char && j < bound_end; j++) {
+            if (*a_char == *b_char && !b_consumed[j]) {
+                b_consumed[j] = true;
+                matches++;
+
+                if (j < b_match_index) {
+                    transpositions++;
+                }
+                b_match_index = j;
+
+                break;
+            }
+            b_char++;
+        }
+        a_char++;
+    }
+
+    if (matches == 0) {
+        *res = 0.0;
+        return 0;
+    }
+
+    *res = (1.0 / 3.0) *
+           (((float)matches / (float)a_numchars) +
+            ((float)matches / (float)b_numchars) +
+            (((float)matches - (float)transpositions) / (float)matches));
+    return 0;
+}
+
+/**
+ * Calculate a “Jaro Winkler” metric.
+ *
+ * Algorithm: <http://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance>
+ *
+ * Like “Jaro” but gives a boost to strings that have a common prefix.
+ *
+ * \note: This implementation does not place a limit on the common prefix
+ * length adjusted for.
+ *
+ * \param a string A
+ * \param b string B
+ * \param res [OUT] a pointer to a float to receive the result
+ * \return -1 on memory allocation failure, otherwise 0
+ */
+int vlc_jaro_winkler(const char *a, const char *b, float* res) {
+    size_t prefix_char_count = 0;
+    float jaro_distance;
+    if (jaro_inner(a, b, &prefix_char_count, &jaro_distance) != 0) {
+        return -1;
+    }
+
+    float jaro_winkler_distance =
+        jaro_distance + (0.1 * (float)prefix_char_count * (1.0 - jaro_distance));
+
+    *res = (jaro_winkler_distance <= 1.0) ? jaro_winkler_distance : 1.0;
+    return 0;
+}


=====================================
src/config/vlc_getopt.h
=====================================
@@ -77,6 +77,12 @@ typedef struct vlc_getopt_s
    false if the option does not take an argument,
    true if the option requires an argument.
 
+   The field `is_obsolete` is a custom VLC addition, tracking whether or not
+   an option is obsolete (such options in VLC remain in existence for some
+   time, with a different error printed compared to that used for unknown
+   options). We store this here as a convenience; it is read by the
+   'suggestion matching' feature, which needs to ignore such options.
+
    If the field `flag' is not NULL, it points to a variable that is set
    to the value given in the field `val' when the option is found, but
    left unchanged if the option is not found.
@@ -92,6 +98,7 @@ struct vlc_option
 {
     const char *name;
     bool has_arg;
+    bool is_obsolete;
     int *flag;
     int val;
 };


=====================================
src/config/vlc_jaro_winkler.h
=====================================
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ * jaro_winkler.c: jaro winkler string similarity algorithm implementation
+ *****************************************************************************
+ * Copyright 2015 Danny Guo
+ * Copyright 2018, 2019 Lyndon Brown
+ *
+ * Authors: Danny Guo <dguo at users.noreply.github.com>
+ *          Lyndon Brown <jnqnfe at gmail.com>
+ *
+ * Licensed under the MIT license. You may not copy, modify, or distribute this
+ * file except in compliance with said license. You can find a copy of this
+ * license either in the LICENSE file, or alternatively at
+ * <http://opensource.org/licenses/MIT>.
+ *****************************************************************************
+ * This file is based upon the Jaro Winkler implementation of the `strsim`
+ * Rust crate, authored by Danny Guo, available at
+ * <https://github.com/dguo/strsim-rs>; more specifically the (as yet un-merged)
+ * optimised copy authored by myself (Lyndon Brown), available at
+ * <https://github.com/dguo/strsim-rs/pull/31>. The code is available under the
+ * MIT license.
+ *****************************************************************************/
+
+#ifndef VLC_JARO_WINKLER_H
+#define VLC_JARO_WINKLER_H 1
+
+/**
+ * Calculate a “Jaro Winkler” metric.
+ *
+ * Algorithm: <http://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance>
+ *
+ * Like “Jaro” but gives a boost to strings that have a common prefix.
+ *
+ * \note: This implementation does not place a limit the common prefix length
+ * adjusted for.
+ *
+ * \param a string A
+ * \param b string B
+ * \param res [OUT] a pointer to a float to receive the result
+ * \return -1 on memory allocation failure, otherwise 0
+ */
+int vlc_jaro_winkler(const char *a, const char *b, float *res);
+
+#endif


=====================================
src/modules/entry.c
=====================================
@@ -409,7 +409,9 @@ static int vlc_plugin_desc_cb(void *ctx, void *tgt, int propid, ...)
         {
             struct vlc_param *param = tgt;
 
-            param->shortname = va_arg(ap, int);
+            char c = va_arg(ap, int);
+            assert(c != '\0' && c != '?' && c != ':');
+            param->shortname = c;
             break;
         }
 


=====================================
src/test/jaro_winkler.c
=====================================
@@ -0,0 +1,101 @@
+/*****************************************************************************
+ * jaro_winkler.c: Tests for our Jaro Winkler algorithm
+ *****************************************************************************
+ * Copyright 2015 Danny Guo
+ * Copyright 2018 Lyndon Brown
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <math.h> /* fabs() */
+
+#include <vlc_common.h>
+#include <vlc_strings.h>
+
+#include "../config/vlc_jaro_winkler.h"
+
+const char vlc_module_name[] = "test_jarowinkler";
+
+# define test1( expected, a, b ) \
+    assert(vlc_jaro_winkler(a, b, &actual) == 0); \
+    failed = (actual != expected); \
+    problems |= failed; \
+    printf("[TEST] expected: %f, actual: %f, accuracy: n/a, result: %s, (a: %s), (b: %s)\n", \
+        expected, actual, (failed) ? "FAIL" : "pass", a, b);
+
+# define test2( expected, a, b, accuracy ) \
+    assert(vlc_jaro_winkler(a, b, &actual) == 0); \
+    failed = (fabs(expected - actual) >= accuracy); \
+    problems |= failed; \
+    printf("[TEST] expected: %f, actual: %f, accuracy: %f, result: %s, (a: %s), (b: %s)\n", \
+        expected, actual, accuracy, (failed) ? "FAIL": "pass", a, b);
+
+int main( void )
+{
+    bool problems = false, failed = false;
+    float actual;
+
+    // both_empty
+    test1(1.0, "", "");
+
+    // first_empty
+    test1(0.0, "", "jaro-winkler");
+
+    // second_empty
+    test1(0.0, "distance", "");
+
+    // same
+    test1(1.0, "Jaro-Winkler", "Jaro-Winkler");
+
+    // diff_short
+    test2(0.813, "dixon", "dicksonx", 0.001);
+    test2(0.813, "dicksonx", "dixon", 0.001);
+
+    // same_one_character
+    test1(1.0, "a", "a");
+
+    // diff_one_character
+    test1(0.0, "a", "b");
+
+    // diff_no_transposition
+    test2(0.840, "dwayne", "duane", 0.001);
+
+    // diff_with_transposition
+    test2(0.961, "martha", "marhta", 0.001);
+
+    // names
+    test2(0.562, "Friedrich Nietzsche", "Fran-Paul Sartre", 0.001);
+
+    // long_prefix
+    test2(0.911, "cheeseburger", "cheese fries", 0.001);
+
+    // more_names
+    test2(0.868, "Thorkel", "Thorgier", 0.001);
+
+    // length_of_one
+    test2(0.738, "Dinsdale", "D", 0.001);
+
+    // very_long_prefix
+    test2(1.0, "thequickbrownfoxjumpedoverx", "thequickbrownfoxjumpedovery", 0.001);
+
+    return (problems) ? -1 : 0;
+}



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/12d1d54909037f44acd198de345197184c28c9c2...dbf81e9acc8ac90286defbc465f6eae5e6f52180

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/12d1d54909037f44acd198de345197184c28c9c2...dbf81e9acc8ac90286defbc465f6eae5e6f52180
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list