Making the best CLI programs - Programming On Unix

Users browsing this thread: 2 Guest(s)
freem
Nixers
(21-06-2021, 06:23 AM)seninha Wrote: Instead of a -h option for different programs to produce human readable values, use a utility, such as z3bra's human, that converts values from its input into human-readable sizes.

For that to work, you need to have the output of your utility trivially parsable by other utities. Most of the CLI tools I know do not check that prerequisite.
And even for those which does, is human(1) usable? For that, you'd need it to be able to identify which ranges of lines are to be converted. This tool (sorry z3bra) also seems to only support power of two units, not power of 10 ones (unit(1) might be able to do that, though, not sure).

seninha Wrote:A double-hyphen (--) on the argument list stops option parsing. Rather than implementing that, use getopt(3), that handles it and much more.

Thanks, but no, I won't use getopt. Because every single code I've read which use it is a nightmare to read. I have my own code.
The difference?
Well, everything related to argument is in a single array, making it much easier to read. Parsing for 3 options or 20 options is a matter of just adding the 17 options, not adding a block of code for each.
I dislike getopt, and I don't like boost::program_options neither, because it's an as horrible code to read, with a lot more overhead.
Another thing I dislike with every getopt program I've seen around is, the lib does not limits the devs, which means you end with various ways to express the same thing. Sometimes you can do something, sometimes you can't, you can never guess and always have to read the fucking manual: short option compression, which might or not when passing it arguments, options might be in the form "-o0", or "-o=0" or "-lol=foo" or "--bla=bar", with the "-lol foo" which might actually be equivalent of "-l -o -l foo".
No, really, getopt is not nice. It's POSIX standard, but clearly not nice (as many other POSIX standard stuff btw).

seninha Wrote:When a program in a script writes to stdout, it is hard to guess which program failed when it does not identify itself. Using the err(1) family of functions writes "progname: errno string: comment", a idiomatic error string that identifies the program by its progname (argv[0]).

A great way to bloat logs. Same about adding timestamps, btw. This is the parent's or the log reader's job to properly handle that kind of stuff, not the program's.
I prefer, by far, when a program shows a simple (Note that this does not really work, instead the __LINE__ must be stringisized. Also note that shitty builders like cmake will put absolute path in __FILE__, but that's only the builder: if they gave the compiler a relative filename, compilers would use that: a relative filename here):

Code:
fprintf( stderr, __FILE__ "[" __LINE__ "]: %s (0x%02x)\n", strerror( errno ), errno );

And let the parent handle that. Not to mention that, putting the name of the binary in there means either hardcoding or introducing a direct link to argv[0] everywhere, with are both solutions I dislike.

Other than those points, I tend to agree :)

One thing I also do (because it's handled by my getopt tool, ofc) is, when there is an error of CLI values, to print what the program understood. This helps fixing problems of unescape shell expansions.

For reference, here's said tool:

The lib (in C++, yes, but C users can still use it, they will simply have to write more utility code as C does not supports templates) and an example of use.

As you can see in example code (line 109 to 148) it takes less than 40 lines, and adding new variables would not make it bigger.
Btw, this code is old... I need to check if I have better working one...
z3bra
Grey Hair Nixers
(15-10-2021, 12:49 PM)freem Wrote: This tool (sorry z3bra) also seems to only support power of two units, not power of 10 ones (unit(1) might be able to do that, though, not sure).

Haha no problem. That's not like I take it personally. I wrote this tool because I didn't find it convenient to fireup bc(1) everytime a tool didn't display file size/bandwidth properly.

For arguments, I use arg.h, a simple macro-based flag parser. It only supports short flags, which is more than enough to me. It's fairly simple to understand and use, and doesn't clutter the code. See it in action here: safe.c:386.