Fossil Forum

RFC: New selection-inverter-option for commands like commit/forget/add
Login

RFC: New selection-inverter-option for commands like commit/forget/add

RFC: New selection-inverter-option for commands like commit/forget/add

(1) By MBL (RoboManni) on 2022-07-12 06:50:11 [source]

It would be great if commands, which allow a list of files, like "commit" or "forget" or "add", could be enhanced with an option to allow for exclude-selections. Example to add all files except the ones with given names or path selections:

fossil.exe add *.txt --NOT foo.txt bar.txt
fossil.exe commit *.c --NOT test*.c

Where the new option --NOT deselects the files listed behind on command line.

In similar way I would love to see some day in future the capability to do additions and removals in preparation of a "new branch commitment" dependent on the "fossil.exe changes" rsp. "fossil.exe status" results like the following example:

fossil.exe forget *.conf --NOT specific.conf specific2.conf
fossil.exe forget --MISSING *.csv

Sometimes it is much easier to say "all but" instead of a long list of individuals. I am missing such an option.

Is such an additional selection-invertor-option worth the effort or is there another easy workaround?

(2.1) By Stephan Beal (stephan) on 2022-07-12 08:37:19 edited from 2.0 in reply to 1 [link] [source]

... is there another easy workaround?

fossil add *
fossil rm that-one-file # reverts the add
fossil rm *
fossil revert that-one-file # reverts the rm

As for checkin, the workaround is easy on non-Windows platforms:

fossil ci -m '....' $(ls -1 *.conf | grep -v that-one-file.conf)

(Presumably PowerShell can do something equivalent.)

Adding --NOT seems like a lot of work to implement just for that rare case where it might be interesting, and then (because it's only rarely used), the user's going to have to go look it up in the docs and that'll take longer than just entering precisely the necessary filenames in the first place. Maybe i'm more pedantic than most about being explicit with which files i'm adding/removing, but --NOT is "not" a feature i've ever felt was missing. Wildcards are absolutely taboo for me in ci unless i've triple-checked them first, e.g. using:

echo fossil ci *.txt<ENTER>
... verify them, then...
^p^a<ESC>d<ENTER>

(That's assuming emacs-style key bindings in the shell.)

Related: when working on repositories which are not my own i've gotten in the habit of always prefixing my checkin command with a # (shell comment character) in order to prevent that i accidentally checkin mid-line or without double-checking my list of files to check in. Tapping ENTER (on purpose or otherwise) will place the command in my history without executing it. Removing the # is as simple as ^a^d (again, assuming emacs key bindings).

(3) By Warren Young (wyoung) on 2022-07-12 15:33:44 in reply to 1 [link] [source]

is there another easy workaround?

For commit, stash the files you want left out of the commit, then pop the stash afterward.

(4) By Florian Balmer (florian.balmer) on 2022-07-14 06:56:50 in reply to 1 [link] [source]

Just recently, I needed a script to expand globs in the Windows command shell, and my preference is WSH JScript so it runs everywhere, see below (the core part to perform the wildcard → regex glob matching was copied from the 3rd party script mentioned in the comments.)

The script could be used like this:

glob *.txt --NOT abc.txt | fossil add --args -

If:

  • The WSH Jscript is embedded in a batch file to be run standalone, for example like this. (I'm only using the script from other batch files, so haven't done this, yet.)
  • The --NOT option is implemented, which is probably no too complicated, i.e. remove the excluded names/globs from the result, probably by comparison with FileSysObj.GetAbsolutePathName().toLowerCase() to ensure correct matching of non-canonicalized paths.
  • The minor caveat is considered that the --args option would treat file names starting with "-" and containing a space as an option-with-argument pair. (Maybe the --args handler should be enhanced to deal with the "--" stop-looking-for-options option, as well?)

/*
**  glob.js
**
**  CREDITS:
**
**    o Code for wildcard expansion based on this "(WSH) file timestamp
**      manipulation utility" script: https://gist.github.com/hanji/3729693
**
**  TODO:
**
**    o Sort output?
**    o -m (fnmatch) mode to check if a pattern matches a string?
**
*/
(function(){
  function usage(){
    WScript.StdErr.WriteLine(
'Expand file name wildcards (globs).\n\n' +
'Usage:\n' +
'  glob [-S] GLOBS...\n\n' +
'Options:\n' +
'  -d    Also match against directory names, instead of just file names.' +
'  -S    Read arguments from STDIN; options with arguments on one line.');
    WScript.Quit(1);
  }
  function print(){
    var args = [];
    args.push.apply(args,arguments);
    WScript.StdOut.WriteLine(args.join('\n'));
  }
  var warn = function(msg){
    if( msg ) WScript.StdErr.WriteLine('WARNING: ' + msg);
  }
  function error(msg,ret){
    if( msg ) WScript.StdErr.WriteLine('ERROR: ' + msg);
    if( typeof ret=='undefined' ) ret = 1;
    WScript.Quit(ret);
  }
  function opt_find(argv,k,v){
    for( var i=0; i<argv.length; i++ ){
      if( argv[i]=='--' ) break;
      if( argv[i]== k ){
        if( v ){
          if( ++i<argv.length ){
            return { 'key': k, 'arg': argv.splice(i-1,2)[1] };
          }
        }else{
          argv.splice(i,1);
          return { 'key': k };
        }
      }
    }
    //return null;
  };
  function opt_left(argv){
    for( var i=0; i<argv.length; i++ ){
      if( argv[i]=='--' ){
        argv.splice(i,1);
        break;
      }
      if( argv[i].slice(0,1)=='-' ){
        return { 'key': argv[i] };
      }
    }
    //return null;
  };
  function argv_expand(argv,inc_dirs){
    var ShellApplication = WScript.CreateObject('Shell.Application');
    var FileSysObj = WScript.CreateObject('Scripting.FileSystemObject');
    var argx=[];
    for( var i=0; i<argv.length; i++ ){
      var arg = argv[i], fSucceeded = false;
      if( arg.match(/[*?]/) ){
        var re = new RegExp(
          FileSysObj.GetFileName(arg)
            .replace(/[-[\]{}()+.,^$|#]/g,'\\$&')   // Quote meta w/o \ / * ?
            .replace(/\*/g,'.*')                    // * = Zero or more chars
            .replace(/\?/g,'.')                     // ? = One single char
            .replace(/^(.*)$/,'\^$1\$')             // Whole filename
          ,'i');                                    // Case insensitive
        var ShellFolder = ShellApplication.NameSpace(
                            FileSysObj.GetParentFolderName(
                              FileSysObj.GetAbsolutePathName(arg)));
        if( ShellFolder ){
          for( var j=0; j<ShellFolder.Items().Count; j++ ){
            var ShellFolderItem = ShellFolder.Items().Item(j);
            if( !inc_dirs && ShellFolderItem.IsFolder ) continue;
            var name = FileSysObj.GetFileName(ShellFolderItem.Path);
            if( name.match(re) ){
              argx.push(
                FileSysObj.BuildPath(FileSysObj.GetParentFolderName(arg),name));
              fSucceeded = true;
            }
          }
        }
      }
      if( !fSucceeded ) argx.push(arg);
    }
    return argx;
  }
  (function main(){
    var argv=[], opt;
    var readStdIn = false;
    var trim = function(s){
      return s.replace(/(?:^\s+)|(?:\s+$)/g,'').replace(/^"(.*)"$/g,'$1');
    };
    for( var i=0; i<WScript.Arguments.length; i++ ){
      argv.push(WScript.Arguments.Item(i));
    }
    if( opt_find(argv,'-S',false) ){
      readStdIn = true;
      while( !WScript.StdIn.AtEndOfStream ){
        var m, arg = WScript.StdIn.ReadLine();
        if( !arg || arg.slice(0,1)=='#' ) continue; // Blank lines and comments.
        else if( arg.slice(0,1)=='-' && (m = /^(\S+) (.+)$/.exec(arg))!==null ){
          argv.push(m[1],trim(m[2]));
        }else{
          argv.push(trim(arg));
        }
      }
    }
    if( argv.length==0 && !readStdIn ) usage();
    var fDirectories = false;
    if( opt = opt_find(argv,'-d',false) ) { fDirectories = true; }
    if( opt = opt_left(argv) ){
      error(
        'Unknown or redundant option, or missing option argument: ' + opt.key);
    }
    argv = argv_expand(argv,fDirectories);
    if( argv.length==0 ) error('No globs to process.');
    print(argv.join('\n'));
  }());
}());

(5) By Florian Balmer (florian.balmer) on 2022-07-14 10:40:06 in reply to 4 [link] [source]

If you don't need globbing syntax for the --NOT part, you can also do this (on Windows):

dir /A:-D /B *.txt | findstr /V /L /C:"abc.txt" | fossil add|rm|ci --args - ...

This works with add, rm and ci, so probably with any other command, as well.

To deal with subdirectories, you can use:

dir /S /A:-D /B *.txt | findstr /V /L /C:"abc.txt" | fossil add|rm|ci --args - ...

This also works with add, rm and ci, so is probably fine with others, too.

There's one subtle difference: dir /S always prints absolute path names, which may have implications on how to craft the findstr expression (which supports both literal or regex matches, with handy options for literal matches to be either at the beginning or end of line). Fossil is fine with absolute path names, and always prints nice and short relative path names in its output.

The absolute/relative path name difference was the only reason for me to write the glob.js script, as it doesn't do anything beyond simple glob expansion.

P.S. Looking over the script, some of it looks a bit sloppy: the -d option is missing in the usage summary, and the function error() vs. var warn = function() declaration discrepancy is a leftover from another script that uses the statement warn = error to deal with the "treat warnings as errors" option.

(6) By Florian Balmer (florian.balmer) on 2022-07-15 07:14:11 in reply to 4 [link] [source]

Maybe the --args handler should be enhanced to deal with the "--" stop-looking-for-options option, as well?

@stephan:

What do you think about this, and do you see a reasonable solution?

My feeling is that it should be possible to feed "literal" lines to STDIN via the --args option, without any preprocessing (such as splitting option-and-option-argument pairs), but I don't know how:

-- --args - : Wrong, --args is not an option.

--args - -- : Auto-magically apply -- before --args if it's the next argument, not intuitive and confusing.

--args -- - --args-are-literal : Clumsy?

Write -- to STDIN : Cumbersome for scripts and programs generating the input.

--arga - -- : Separate option with "A" in --arga for "append STDIN to ARGV", instead of "S" in --args for "substitute STDIN in ARGV", so that -- could take effect, as would have to be checked in the processing code.

It looks like dropping the splitting of option-and-option-argument pairs is a reasonable burden to scripts or humans generating the input, i.e. they just need to insert another newline, but then the missing effect of -- could still cause literal arguments starting with - to be interpreted as options, later.

Tricky!

In my posted glob.js script, I'm using an even fancier approach, allowing comments and extra leading spaces, and quotes to suppress the splitting of option-and-option-argument pairs. But that's also not a good solution, as scripts generating the input would have to enclose each line in double quotes!

My approach is from a "makedeps" script I've created, with a fancy way to read input from an inline file:

makedeps:
@cscript //nologo //U makedeps.js -S < << > dependencies
# Options
-o "OBJ"
-b "OUT"
-t "TOP"
-I "inc"
-I "lib"
--
# Source files
"sample1.c"
"sample2.c"
# Resource scripts
"sample1.rc"
"sample2.rc"
<<UNICODE,NOKEEP

(7) By Stephan Beal (stephan) on 2022-07-15 09:12:56 in reply to 6 [link] [source]

What do you think about this, and do you see a reasonable solution?

i've been asking myself the same thing since you mentioned it and don't yet know for sure ;). i'm not really keen on the idea of having --args do unusual acrobatics. It's worked the way it currently does since 12+ years. To change that just for the sake of a highly unusual use case seems unwarranted.

It looks like dropping the splitting of option-and-option-argument pairs is a reasonable burden to scripts or humans generating the input, i.e. they just need to insert another newline, but then the missing effect of -- could still cause literal arguments starting with - to be interpreted as options, later

i don't see that as being fossil's problem to solve. If someone wants to create ultra-elaborate argument lists, they can craft them in a tsxt editor. If fossil tries to do any magical handling, that magic will be the wrong magic for some users. Generally speaking, software trying to be "too clever" is a great source of user frustration.

That said: there is no arguing with working code and i would not fight against any patches which make it more useful, provided they don't break stuff.

(8) By Florian Balmer (florian.balmer) on 2022-07-15 11:38:48 in reply to 7 [link] [source]

Yes, the danger of breaking stuff is real, but on the other hand, who is aware of the nifty --args? ;-) Also, making --args work after -- is really tricky, and so is checking for -- before full option parsing, as -- could be a valid option argument:

fossil ci -m --

Another new idea:

  • If the first line of input is + or ! or similar, the current behavior is retained. This should be easy to achieve with scripts or hand-crafted text files.

  • Otherwise, the arguments read from STDIN are appended to the end of ARGV after option parsing has completed, which would also (implicitly) add a -- in front of the STDIN arguments. This makes the preferred way to process file lists from simple/single commands.

Unless they start naming their files + or !. Hm.

This has to settle a little.

(9) By Florian Balmer (florian.balmer) on 2022-07-17 09:19:01 in reply to 7 [link] [source]

... highly unusual use case ...

Agree! But, we're already doing quite a the dance so people can type1:

fossil add -- -filename

Instead of:

fossil add ./-filename

My feeling is that this should be possible with --args even more, so it's safe to process lists of file names output by other commands without having to worry about any special cases.

My latest idea is to add another --fileargs option that works like --args, but without any splitting of "-word0 word1 ..." lines into multiple arguments, and with auto-magic prefixing of "./" to lines starting with "-". The auto-magic prefixing could be transparently documented, and --args and --fileargs could even be combined (unless they both specify "-" as the input file to read from STDIN).

If I find the time for this, I may give it a try. I'd expect the necessary changes to be simple and non-invasive, relying mostly on the existing --args code.


  1. ^ I know with *nix shells it's easier to inject script output into command lines, so there's less need to resort to STDIN, and the "--" option blocker is available if required.

(10) By Florian Balmer (florian.balmer) on 2022-07-18 11:15:47 in reply to 7 [link] [source]

Also, making --args work after -- is really tricky, and so is checking for -- before full option parsing, as -- could be a valid option argument:

fossil ci -m --

Hm, this "bug" is already there:

> fossil ci -m -- --args -
unrecognized command-line option or missing argument: --args

It looks like expand_args_option() doesn't take into account whether any given -- argument might be an option argument or a true option stopper.

But find_option() has the same problem:

> type src\main.c

⋮
/*
** COMMAND: test-opt
**
** Usage: %fossil test-opt ...
*/
void cmd_testopt(void){
  const char *a = find_option("a",0,1);
  const char *b = find_option("b",0,1);
  verify_all_options();
  fossil_print("a=%s\nb=%s\nargv[2]=%s\n",a,b,(g.argc>2)?g.argv[2]:NULL);
}
⋮

> fossil test-opt -a -- -b -- -- c
a=--
b=--
argv[2]=c

> fossil test-opt -b -- -a -- -- c
unrecognized command-line option or missing argument: -a

Whereas:

$ cat testopt.c
#include <getopt.h>
#include <stdio.h>
int main (int argc,char **argv)
{
  int ch;
  const char *a = NULL, *b = NULL;
  while ((ch = getopt(argc,argv,"a:b:")) != -1)
    switch (ch) {
    case 'a':
      a = optarg;
      break;
    case 'b':
      b = optarg;
      break;
    }
  argc -= optind;
  argv += optind;
  printf("a=%s\nb=%s\nargv[1]=%s\n",a,b,(argc>0)?argv[0]:NULL);
  return 0;
}
$ cc testopt.c
$ ./a.out -a -- -b -- -- c
a=--
b=--
argv[1]=c
$ ./a.out -b -- -a -- -- c
a=--
b=--
argv[1]=c
$

It's been a while since I've looked into the options of option parsing, but the Fossil way seemed refreshingly simple, especially when dealing with global options.

However, it looks like it's not possible to implement -- correctly without knowledge of the options to accept option arguments.

It's all corner cases, I agree, but before looking further into --fileargs, shouldn't we just drop -- handling and require all file names to not start with a dash? That way Fossil wouldn't "halfheartedly" try to do something that may bite back, given the "unpredictability"?

(11) By Stephan Beal (stephan) on 2022-07-18 11:38:26 in reply to 10 [link] [source]

It looks like expand_args_option() doesn't take into account whether any given -- argument might be an option argument or a true option stopper.

Nope - the double-dash handling is a recent addition (sometime in the past 2 years) and --args was not (IIRC) modified in any way for that case (didn't need to be because it rewrites the argument list very early on). IIRC, but am away from the computer so can't easily confirm, the double-dash is not processed until find_option() is called, which happens long after --args processing.

fossil test-opt -b -- -a -- -- c

That falls into the category of "garbage in, garbage out" ;). The args processor cannot distinguish the second case because it does not know that -b will require a value so it has no choice except to treat the first -- like a normal double dash. The first case works because find_option() knows that -a has a value and it removes both -a and its value from g.argv. All of that works fine so long as it's not misused via trickery such as multiple double-dashes. The only(?) way to completely eliminate such corner cases is to reengineer the flag handling such that all flags for a given command are registered/known before flag processing starts.

(12) By Florian Balmer (florian.balmer) on 2022-07-18 12:00:01 in reply to 11 [link] [source]

... the double-dash is not processed until find_option() is called, which happens long after --args processing ...

Ok, but then --args implicitly chokes on --, otherwise the following two cases would work the same:

> echo x|fossil test-echo -U -me- --args -
g.nameOfExe = [C:\...\fossil.exe]
argv[0] = [fossil]
argv[1] = [test-echo]
argv[2] = [x]
> echo x|fossil test-echo -U -- --args -
g.nameOfExe = [C:\...\fossil.exe]
argv[0] = [fossil]
argv[1] = [test-echo]
argv[2] = [--args]
argv[3] = [-]

... "garbage in, garbage out" ;)

I partially agree. But in the end this is quite subjective, i.e. file names starting with "-" look much more like garbage (when they could be prefixed with "./" to avoid all problems) to me than "--" as an option argument, for example to specify some kind of output separator, or some "not available" comment, or similar.

(13) By Stephan Beal (stephan) on 2022-07-18 22:57:22 in reply to 12 [link] [source]

otherwise the following two cases would work the same:

> echo x|fossil test-echo -U -me- --args -
> echo x|fossil test-echo -U -- --args -

Looking at main.c:expand_args_option(), it explicitly handles the -- case if that appears before --args. Thus for your second example, --args is, _at the point where --args is processed_, treated as a non-flag because it comes after a--. The code which handles that does not know that-Urequires a value, so-U's value of--is treated as the end of arguments. If, later on,-U --is consumed byfind_option("U",...,1),--argsbecomes just a normal flag with the value-`, but that happens long after expand_args_option() has been run (one of the very first things fossil does).

That's simply a limitation of the flag-handling model in fossil. It was not designed with -- handling in mind and that was grafted on much later, subject to the limits the flag-handling infrastructure.

(14) By Florian Balmer (florian.balmer) on 2022-07-19 04:23:00 in reply to 13 [link] [source]

Thanks for your time to look into this!

You have now explained several times how the code works, and why it has evolved like this. I already knew this from following the Fossil development, reading the code and experimenting, but was trying to focus on the perspective of the Fossil user who is not necessarily familiar with the internals, and for whom the behavior may cause surprises.

Anyway, since you haven't closed this thread with your Management Summary ;-) my final questions:

  1. Is --args intended to be a "meta-option" that also works behind -- (as you initially stated, but later corrected), and should it be fixed to work like this?
  2. If yes and yes, should --args stop splitting option-and-option-argument pairs, for this would not work well if there was a -- in front of it?

If 3x yes, then a separate --fileargs is not required.


Another intersting alternative to `getopt':

(15) By Stephan Beal (stephan) on 2022-07-19 13:12:22 in reply to 14 [link] [source]

Is --args intended to be a "meta-option" that also works behind -- (as you initially stated, but later corrected), and should it be fixed to work like this?

My current instinct is yes, the check for "--" should be removed from the --args expansion, and simply let find_option() handle it. That would eliminate some of this weirdness and provide more consistency.

If yes and yes, should --args stop splitting option-and-option-argument pairs, for this would not work well if there was a -- in front of it?

We can't(?) change its splitting behavior without a risk of breaking things. We can, however, certainly remove the "--" check from that step of args processing because its behavior is current unpredictable without digging around in the code. i'll make that change later today.

If 3x yes, then a separate --fileargs is not required.

i personally still don't see the requirement for it, other than "it might be interesting to have for one use case in a million," but you'll get no debate from me if it's useful for you.

(16) By Florian Balmer (florian.balmer) on 2022-07-20 04:21:21 in reply to 15 [link] [source]

We can, however, certainly remove the "--" check from that step of args processing because its behavior is current unpredictable without digging around in the code. i'll make that change later today.

There's no code change in this commit, only the comment was modified, or am I missing something?

We can't(?) change its splitting behavior without a risk of breaking things.

While I am usually in favor of backwards compatibility, this is a risk I'd take: the splitting is only rudimentary and not intuitive, thus probably not something people are relying on.

Consider the following text file to prepare the arguments to a check-in operation:

commit
-m                "This is a comment."
--date-override   "2022-07-07T07:07:07Z"

This gets you:

argv[1] = [commit]
argv[2] = [-m]
argv[3] = [               "This is a comment."]
argv[4] = [--date-override]
argv[5] = [  "2022-07-07T07:07:07Z"]

Nothing that Fossil is able to process reasonably, so you need:

commit
-m This is a comment.
--date-override 2022-07-07T07:07:07Z

To get an useful result:

argv[1] = [commit]
argv[2] = [-m]
argv[3] = [This is a comment.]
argv[4] = [--date-override]
argv[5] = [2022-07-07T07:07:07Z]

But the version not relying on the splitting somehow looks tidier and more readable:

commit
-m
This is a comment.
--date-override
2022-07-07T07:07:07Z
argv[1] = [commit]
argv[2] = [-m]
argv[3] = [This is a comment.]
argv[4] = [--date-override]
argv[5] = [2022-07-07T07:07:07Z]

i personally still don't see the requirement for it, other than "it might be interesting to have for one use case in a million," but you'll get no debate from me if it's useful for you.

I'm not particularly bugged with that one special case (but it does exist, and guess where I found this recommendation to prefix "-" to file names?), but more with the fact that we have a way to specify "no matter what, don't try to treat the remaining arguments as options" for the command line, but not for STDIN, when I think it's more likely for machine-generated file listings to be fed to STDIN, and at the same time prefixing "./" to escape option parsing is not quite as simple as it is for the command line.

(17) By Florian Balmer (florian.balmer) on 2022-07-20 04:54:27 in reply to 16 [link] [source]

There's no code change in this commit, only the comment was modified, or am I missing something?

Yes, I've missed something -- the code change. Sorry.

(18) By Stephan Beal (stephan) on 2022-07-20 09:51:20 in reply to 16 [link] [source]

While I am usually in favor of backwards compatibility, this is a risk I'd take: the splitting is only rudimentary and not intuitive, thus probably not something people are relying on.

While i tend to agree, it has worked fine the way it is since almost 11 years, and that's a lot of time for people to get used to, and rely on, the way it currently works. We can, as demonstrated in your examples, break it completely if we try to, but if it's used "as intended," it serves its purpose. i agree entirely that it's not as intuitive or elegant as your second example.

i'm not at all against creating a new variant and deprecating this one, but outright replacing it with a new way of tokenizing would make me nervous in terms of expected backlash from users whose cron jobs are broken by changes in its behavior. My recommendation for a replacement would be -@ filename, as the @ prefix is conventionally used to mean "treat this argument as a file containing additional arguments". e.g. by zip, jar, emcc, and a number of other tools i've used over the years offer this as a way to feed in lists of files, in particular for OS shells which have very limited argument length limits (ZIP for DOS had that support for that reason).

but more with the fact that we have a way to specify "no matter what, don't try to treat the remaining arguments as options" for the command line, but not for STDIN, when I think it's more likely for machine-generated file listings to be fed to STDIN, and at the same time prefixing "./" to escape option parsing is not quite as simple as it is for the command line.

Now that the expand_args_option() no longer handles -- by itself, does that resolve that conflict for you?

(19) By Florian Balmer (florian.balmer) on 2022-07-20 11:20:55 in reply to 18 [link] [source]

My recommendation for a replacement would be -@ filename ...

I was also thinking of this, but feared this might introduce another "reserved (file) name" -- i.e. if the -@ argument is treated as a low-level "meta-option" exactly the same way as --args, not taking into account --, then the argument -@ can't be used as a file name or option argument any more, unless via STDIN. So maybe -@ should be handled in verify_all_options(), where it would be disabled by --? But then the different semantics of --args and -@ regarding -- may also seem odd and confusing.

... users whose cron jobs are broken by changes in its behavior ...

Reasonable.

Now that the expand_args_option() no longer handles -- by itself ...

In retrospect it looks like expand_args_option() was the only place to care for -- for 8 years before the find_option() family learned about it!

... does that resolve that conflict for you?

Hm, ... no:

> echo -a file name with both dash and spaces| fossil test-echo -- --args -
g.nameOfExe = [C:\...\fossil.exe]
argv[0] = [fossil]
argv[1] = [test-echo]
argv[2] = [--]
argv[3] = [-a]
argv[4] = [file name with both dash and spaces]

In this case, both "-a" and "file name with both dash and spaces" will be treated as non-options, but that's still wrong, there should be only one non-option with the value "-a file name with both dash and spaces". Even putting -- in front of --args can't undo the extra splitting when commands other than test-echo call verify_all_options().

Only by disabling these lines would it work:

> echo -a file name with both dash and spaces| fossil test-echo -- --args -
g.nameOfExe = [C:\...\fossil.exe]
argv[0] = [fossil]
argv[1] = [test-echo]
argv[2] = [--]
argv[3] = [-a file name with both dash and spaces]

My head is buzzing, i.e. how complex something so "simple" can be. And I just found out there's public versions of `getopt.c' older than me, yet still I'm struggling with this!

Maybe just accepting the quirks, instead of introducing more quirks with clever quirks-arounds ... ?

(20) By Stephan Beal (stephan) on 2022-07-20 11:40:12 in reply to 19 [link] [source]

Maybe just accepting the quirks, instead of introducing more quirks with clever quirks-arounds ... ?

An excellent summary :). Like SVN and CVS: so long as its limits are accounted for, fossil's current argument handling is completely sufficient for "normal" use cases and day-to-day use.

(21) By Florian Balmer (florian.balmer) on 2022-08-07 08:30:21 in reply to 20 [link] [source]

Not to restart the discussion, just keeping some notes:

Another convention is to append option arguments with an "=" to options1, to fix option arguments that look like options but can't be hidden behind "--":

--option="--some goofy name"

In this case, find_option() and verify_all_options() would never see "--some goofy name" as a separate option. But expand_args_option() would still choke, and no improvements for regular (non-option) arguments.


  1. ^ This doesn't seem too complicated to implement. The current convention to separate by spaces would continue to work, but there's another possibly helpful way to hide from the option parser, if needed.