newLISP Fan Club

Forum => newLISP in the real world => Topic started by: TedWalther on July 22, 2011, 02:19:18 AM

Title: POSIX command line argument processing
Post by: TedWalther on July 22, 2011, 02:19:18 AM
I have made a module called "getopts".  It doesn't use its own context.  I don't think it needs to.  If people can try it out, it does the following:



It parses command line options in the following forms:



-f filename
-ffilename (-f filename)
-avh (-a -v -h)
-avfboo (-a -v -f boo)
--long-option
--long-option with-argument
--long-option=with-argument (--long-option "with-argument")
-- (everything after -- is ignored)


Anything that is not an option, or an argument to an option, is just accumulated to a list.  At the end of the option processing, this accumulated list is returned for your own custom processing.



There is just one possible glitch.  I start the argument parsing at index position 2 of main-args.  For my scripts, this works well.



As a bonus, the way it is set up, the "usage" function, automatically lists all the options that you have specified.  Also, unlike in C, you don't call getopt repeatedly.  The getopts function does it all in one pass.  Here is an example of how to use it:



(module "getopts.lsp")

(short-opt "v" (++ verbosity) nil "Increase verbosity")
(short-opt "q" (setq verbosity 0) nil "Quiet")
(short-opt "?" (usage) nil "Print this usage message")
(short-opt "h" (usage) nil "Print this usage message")
(short-opt "o" (setq output-file arg) "file" "Output file")
(long-opt "help" (usage) nil "Print this usage message")
(long-opt "quiet" (setq verbosity 0) nil "Quiet")
(long-opt "verbose" (++ verbosity) nil)
(long-opt "output" (setq output-file arg) "file" "Output file")

(println (getopts)) ; this shows the command line datum that weren't options
(println "Verbosity: " verbosity)
(println "Output To: " output-file)
(exit)


Example usage:



$ ./test.lsp --output foo -obar -v --quiet -vv arg1 arg2
("arg1" "arg2")
Verbosity: 2
Output To: bar


There is just one issue.  If I pass an argument to newlisp, it changes the starting point for processing in main-args.  I see no way to detect this.



#!/usr/bin/newlisp

$ ./test.lsp foo bar
main-args: "/usr/bin/newlisp" "./test.lsp" "foo" "bar"

#!/usr/bin/newlisp -m 50 -s 1024

$ ./test.lsp foo bar
main-args: "/usr/bin/newlisp" "-m 50 -s 1024" "./test.lsp" "foo" "bar"


See how it differs?  Wouldn't it be better if there was an empty string there always, in index position 1, if there are no arguments passed to the newlisp interpreter?
Title: Re: POSIX command line argument processing
Post by: Lutz on July 22, 2011, 08:10:08 AM
Different UNIX behave differently on how they treat arguments in the #! line of a script file:



With this "args" program:

#!/usr/bin/newlisp -m 50 -s 1024

(println (main-args))

(exit)

you get different output on different platforms:



on Mac OSX:

~> ./args foo bar
("/usr/bin/newlisp" "-m" "50" "-s" "1024" "./args" "foo" "bar")

on FreeBSD, OpenBSD and Linux:

~> ./args foo bar
("/usr/bin/newlisp" "-m 50 -s 1024" "./args" "foo" "bar")

Only Mac OSX does it "correctly". newLISP just passes on whatever the argv[] array is offered by the standard libraries and C startup code, assuming that this is the excpected behavior on that platform.



But I don't think it really matters, because starting with the script name "./args" the structure of the list is the same.
Title: Re: POSIX command line argument processing
Post by: TedWalther on July 22, 2011, 05:08:18 PM
Thank you for that feedback.  I am modifying the getopts function so now you have to pass in the list of arguments.  That will allow it to work across all platforms.
Title: Re: POSIX command line argument processing
Post by: TedWalther on July 24, 2011, 01:19:54 AM
I have updated the getopts module, and anyone can now download and test it here:



http://dpkg.reactor-core.org/modules/



I hope that everyone finds it useful.  Especially if you want to distribute a newlisp script as a commandline utility, this module makes it easy to support the GNU coding standards, and pass information into your script through the command line.



Please look at it, test it, and comment.  I included sample code so you can cut and paste to get started.



Ted