;;;; -*- scheme -*- ;;;; command-line-processor.scm --- command-line options processing ;;;; ;;;; Copyright (C) 1998, 2001, 2006, 2009, 2011, 2020 ;;;; Free Software Foundation, Inc. ;;;; ;;;; This library 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 3 of the License, or (at your option) any later version. ;;;; ;;;; This library 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 library; if not, write to the Free Software ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ;;;; 02110-1301 USA ;;; Author: Dale Mellor May, 2020 ;;; Commentary: ;;; Where the Guile (ice-9 getopt-long) module, modelled after the GNU C ;;; libraryʼs ‘getopt_long’ function, allows an application to construct ;;; a grammar prescribing the decomposition of the command-line options, ;;; this module, inspired by the C libraryʼs ‘argp’ parser, gives the ;;; application a higher-level paradigm in which the command-line ;;; processing is specified declaratively. This includes enough of the ;;; application meta-data and some fragmentary help strings for the ;;; completely automatic generation of responses to GNU-standard ;;; ‘--help’, ‘--version’ and ‘--usage’ options, thus alleviating the ;;; need of the application itself to deal with these things. ;;; ;;; The module has three specific aims. ;;; ;;; 1) Provide higher-level declarative interface, easier to use. ;;; ;;; 2) Automatically respond to --help, --version and --usage ;;; options. ;;; ;;; 3) Allow amalgamation of specifications, so that an application ;;; can mix in requirements from modules into its own option ;;; specification--THIS IS NOT CURRENTLY IMPLEMENTED. ;;; ;;; There is just one function which needs to be called to get all of ;;; this functionality: it is ‘process-command-line’, and has the side ;;; effect that new variable bindings appear in the current module ;;; corresponding to all the options. For example, if a declared option ;;; is ‘--do-this’, then a variable called, literally, ‘--do-this’ will ;;; be injected in the current namespace and will have the value ;;; provided on the command-line, or simply #t or #f to indicate whether ;;; or not that option was present on the command line. ;;; ;;; Alternatively, it is possible to create and compose the ;;; specification in separate steps, and then call the above method with ;;; the results. The functions ‘command-line-specification’ and ;;; ‘merge-command-line-specifications’ are provided to this end. ;;; (process-command-line COMMAND-LINE SPECIFICATION) ;;; Process the COMMAND-LINE according to the application SPECIFICATION. ;;; ;;; COMMAND-LINE is a list of strings, such as that returned from the ;;; core ‘command-line’ function. ;;; ;;; SPECIFICATION is a form holding a space-separated mix of selection ;;; words followed by their respective declarations. The selection ;;; words are ‘application’, ‘author’, ‘bug-address’, ‘copyright’, ;;; ‘help-preamble’, ‘help-postamble’, ‘license’, ‘option’, ‘usage’ and ;;; ‘version’, and can appear in any order. ;;; ;;; ‘application’ should be followed by a string: the name of the ;;; application with possibly the package name in ;;; parentheses afterwards ;;; ‘author’ should be followed by a string giving the name of one of ;;; the packageʼs authors. This selection word can be ;;; repeated as many times as necessary to provide the names ;;; of all authors. ;;; ‘bug-address’ should be followed by a string giving the URL of a ;;; contact-point for sending bug reports, such as an ;;; e-mail address or web address of bug-tracking system ;;; interface ;;; ‘copyright’ should be followed by a string containing a list of ;;; years and an entity to whom the copyright is assigned. ;;; This may be repeated to list other assignees ;;; ‘help-preamble’ should be followed by a number of strings which ;;; make up a short paragraph of text displayed before ;;; a full list of the available program options ;;; ‘help-postamble’, like the preamble, is followed by strings which ;;; make up a paragraph of text, shown after the list ;;; of options ;;; ‘license’ can be followed by one of the words ‘GPLv3’ [this is ;;; currently the only standard choice implemented], or else ;;; a string which briefly gives out the terms of the license ;;; ‘option’ is followed by an option declaration, described below ;;; ‘usage’ is followed by a string describing the usage of the ;;; application on one line ;;; ‘version’ is followed by a string providing the current version ;;; number of this program ;;; ;;; The ‘option’ declaration is followed by another form bracketed by ;;; parentheses and holding a space-separated mix of declarations (order ;;; irrelevant). ;;; ;;; A word beginning with two hyphens, an optional exclamation point, ;;; alpha-numeric characters, an optional equals sign, and an ;;; optional further word. There must be exactly one of these, and ;;; they determine the long name of the option. An exclamation point ;;; indicates that the option MUST appear on the command line, an ;;; equals indicates that the option MUST have a value unless it is ;;; followed in the specification by a value, in which case the value ;;; on the command-line is optional and the one in the specification ;;; will be taken as the default when not given on the command line. ;;; ;;; A word comprised of one hyphen and one letter or number. There ;;; can be exactly zero or one of these, and it declares that the ;;; option has this short form available on the command-line. As a ;;; very special exception: if you want to use ‘-i’ as an option, it ;;; must be specified with the identifier ‘short-i’ (a naked /-i/ is ;;; read as a complex number); ditto ‘short-I’ for ‘-I’. ;;; ;;; A number of strings which are catenated together to provide a ;;; short, succinct description of the option. These strings should ;;; be approximately half the width of a page, i.e. about 40 ;;; characters. ;;; ;;; A function which will be used as a predicate to decide if a value ;;; is allowable for this option. There should be zero or one of ;;; these. ;;; ;;; For the precise presentation of options on the command-line, the ;;; reader should refer to the description of the ‘getopt-long’ module, ;;; which underlies the present one. ;;; ;;; At this point a short example is in order. The main entry point for ;;; the GNU Mcron program has as its first clause ;;; ;;; (process-command-line (command-line) ;;; application "mcron" ;;; version "1.4" ;;; usage "[OPTIONS]... [FILES]..." ;;; help-preamble ;;; "Run an mcron process according to the specifications in the FILE... " ;;; "(`-' for standard input), or use all the files in ~/.config/cron " ;;; "(or the deprecated ~/.cron) with .guile or .vixie extensions.\n" ;;; "Note that --daemon and --schedule are mutually exclusive." ;;; option (--daemon -d ;;; "run as a daemon process") ;;; option (--stdin=guile short-i (λ (in) (or (string=? in "guile") ;;; (string=? in "vixie"))) ;;; "format of data passed as standard input or file " ;;; "arguments, 'guile' or 'vixie' (default guile)") ;;; option (--schedule=8 -s string->number ;;; "display the next N (or 8) jobs that will be run") ;;; help-postamble ;;; "Mandatory or optional arguments to long options are also mandatory or " ;;; "optional for any corresponding short options." ;;; bug-address "bug-mcron@gnu.org" ;;; copyright "2003, 2006, 2014, 2020 Free Software Foundation, Inc." ;;; license GPLv3) ;;; ;;; after which there are four new variable bindings in the present ;;; namespace: --daemon, --stdin, --schedule and --! (the latter holds ;;; all the command-line arguments that did not partake in option ;;; processing) whose values depend on the specific command-line options ;;; the end user furnished. ;;; (command-line-specification SPECIFICATION) ;;; Compiles an object which encapsulates the given SPECIFICATION. ;;; ;;; For details of how to give a SPECIFICATION, see the description of ;;; the full ‘process-command-line’ function above. The return from ;;; this method can be used in the partial version of ;;; ‘process-command-line’ described below, and in the following ;;; ‘merge-command-line-specifications’ function. ;;; (merge-command-line-specifications SPECIFICATION_OBJECT ...) Make a ;;; single specification object which embodies the amalgamation of all ;;; of the specification objects given as arguments. ;;; ;;; Order is important: if two option items specify the same short form ;;; for the option (a single letter), then only the first option will ;;; actually have that short form available at the command-line. ;;; Similarly, if two options have exactly the same name, the second (or ;;; later) ones will have a numerical digit appended to their name. ;;; (process-command-line COMMAND-LINE SPECIFICATION-OBJECT) Perform ;;; exactly the same function as the full ‘process-command-line’ ;;; function described above, but takes a pre-made specification object ;;; produced using the two functions above. ;;; Bugs/To do ;;; ;;; 1) This stuff currently only works in the top-level module. ;;; ;;; 2) Want to be able to amalgamate command-line specifications from ;;; different modules. Will need to get to the bottom of the first ;;; issue before we can tackle this one (somehow need to put the ;;; --option variable bindings into the right places, or at least ;;; replicate them all in all modules which want to do some processing ;;; of the command line). ;;; ;;; 3) Want more license boilerplate text; currently we only have GPLv3. ;;; Code: (define-module (mcron command-line-processor) #:use-module (srfi srfi-1) ;; fold #:use-module (srfi srfi-9) ;; records #:use-module (srfi srfi-9 gnu) ;; set/get-fields #:use-module (mcron getopt-long) #:use-module (ice-9 regex) #:export (specific option item obtain-getopt-long-results process-getopt-long-results ;; These are the real public exports. process-command-line command-line-specification merge-command-line-specifications)) (define-record-type <> (make-specification- preamble postamble copyright authors options) specification? (name spec:name spec:set-name!) (version spec:version spec:set-version!) (usage spec:usage spec:set-usage!) (preamble spec:preamble spec:set-preamble!) (postamble spec:postamble spec:set-postamble!) (bug-address spec:bugs spec:set-bugs!) (copyright spec:copyright spec:set-copyright!) (license spec:license spec:set-license!) (authors spec:authors spec:set-authors!) (options spec:options spec:set-all-options!)) ;; We initialize the fields which are supposed to be lists, but ;; generally this procedure should *not* be considered to be producing a ;; properly specified <> record. (define (make-specification) (make-specification- '() '() '() '() '())) (define-record-type <