File: getopts.lsp

package info (click to toggle)
newlisp 10.7.5-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 6,292 kB
  • sloc: ansic: 33,280; lisp: 4,181; sh: 609; makefile: 215
file content (174 lines) | stat: -rw-r--r-- 6,902 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
;; @module getopts.lsp
;; @description Parse short and long command line options according to POSIX rules
;; @version 1.0 initial release
;; @author Ted Walther, July 2011
;;
;; POSIX options come in 4 types; long and short, with or without an extra argument.
;;
;; Short options are just a single letter with a <tt>-</tt> in front. '-a -v -h' are single options.  Short options can be collapsed together.  '-a -v -h' is the same thing as '-avh'
;;
;; A short option that takes an argument can take the following two forms: '-fmyletter.txt' is the same as '-f myletter.txt'
;;
;; Long options start with '--'.  '--quiet' and '--help' are good examples.
;;
;; A long option that takes an argument can be in the following two forms: '--file=myletter.txt' is the same as '--file myletter.txt'
;;
;; Here is an example of how to use this module.  It includes a bunch of standard options that every GNU program should support.
;; 
;; @example
;;
;; (module "getopts.lsp")
;; 
;; (setq version-string "Version: 1.0 (2011)")
;;
;; (shortopt "V" (getopts:die version-string) nil "Print version string")
;; (shortopt "v" (++ verbosity) nil "Increase verbosity")
;; (shortopt "q" (setq verbosity 0) nil "Quiet")
;; (shortopt "?" (getopts:usage) nil "Print this help message")
;; (shortopt "h" (getopts:usage) nil "Print this help message")
;; (shortopt "o" (setq output-file getopts:arg) "file" "Output file")
;; (longopt "help" (getopts:usage) nil "Print this help message")
;; (longopt "quiet" (setq verbosity 0) nil "Quiet")
;; (longopt "verbose" (++ verbosity) nil)
;; (longopt "version" (getopts:die version-string) nil "Print version string")
;; (longopt "output" (setq output-file getopts:arg) "file" "Output file")
;; 
;; (println (main-args))
;; (println (getopts (2 (main-args))))
;; (println "Verbosity: " verbosity)
;; (println "Output To: " output-file)
;; (exit)

;; @syntax (shortopt <opt> <action> <arg?> <desc>)
;; @param <opt> The single letter option
;; @param <action> The code to execute when the option is found
;; @param <arg?> 'nil' if the option doesn't take an argument, otherwise a string that describes the type of argument the option takes.
;; @param <desc> A string that describes the option.  This is used by the 'usage' function.
;; @example
;; (shortopt "o" (setq output-file getopts:arg) "file" "Output file")
;; ...
;; $ ./myscript.lsp -ofoo.txt -q
;; $ ./myscript.lsp -qofoo.txt
;; $ ./myscript.lsp -o foo.txt -q
;; $ ./myscript.lsp -qo foo.txt
(define-macro (shortopt opt action arg? desc)
  (if (assoc opt getopts:short)
      (setf (assoc opt getopts:short) (list opt (list action arg? (or desc ""))))
    (push (list opt (list action arg? (or desc ""))) getopts:short)))

;; @syntax (longopt <opt> <action> <arg?> <desc>)
;; @param <opt> The long option
;; @param <action> The code to execute when the option is found
;; @param <arg?> 'nil' if the option doesn't take an argument, otherwise a string that describes the type of argument the option takes.
;; @param <desc> A string that describes the option.  This is used by the 'usage' function.
;; @example
;; (longopt "output" (setq output-file getopts:arg) "file" "Output file")
;; ...
;; $ ./myscript.lsp --output=foo.txt --quiet
;; $ ./myscript.lsp --verbose --output foo.txt
(define-macro (longopt opt action arg? desc)
  (if (assoc opt getopts:long)
      (setf (assoc opt getopts:long) (list opt (list action arg? (or desc ""))))
    (push (list opt (list action arg? (or desc ""))) getopts:long)))

;; @syntax getopts:arg
;;
;; The variable <getopts:arg> holds the argument to the option, for those options
;; which take an argument.  This is useful inside the <action> code, so you can make
;; use of the argument.  For instance, '--prefix=/usr/bin' on the command line, would
;; leave the value '/usr/bin' in <getopts:arg>, and your <action> code could store the
;; value or act on it in some other way.

(context 'getopts)

(define short (list))
(define long (list))

;; @syntax (getopts:usage)
;; @return Exits script with a value of 0
;; Prints out every command line option that has been registered with the getopts module.
;;
;; @example
;; (shortopt "?" (getopts:usage) nil "Print this help message")
;; ...
;; $ ./myscript.lsp -?
;; ==>
;; Usage: ./myscript.lsp [options]
;; 	 -o file           	Output file
;; 	 -h                	Print this help message
;; 	 -?                	Print this help message
;; 	 -q                	Quiet
;; 	 -v                	Increase verbosity
;; 	--output file           	Output file
;; 	--verbose                	
;; 	--quiet                	Quiet
;; 	--help                	Print this help message
;;

(define (usage)
  (println "Usage: " (main-args 1) " [options]")
  (dolist (o short) (println (format "\t -%s %-15s\t%s" (o 0) (or (o 1 1) "") (o 1 2))))
  (dolist (o long) (println (format "\t--%s %-15s\t%s" (o 0) (or (o 1 1) "") (o 1 2))))
  (exit 0))

;; @syntax (getopts:die <format-string> [<format options...>])
;; @return Exits script with a value of 1
;; Prints an error message, then exits.  Syntax is exactly the same as the 'format' function.

(define-macro (die)
  (println (eval (append (list 'format) (args)))) (exit 1))

(define (getopts:getopts arglst)
  (let (unoption-args (list))
    (while arglst
      (let (a (pop arglst))
	(cond
	 ((regex "^--$" a)
	  (setq unoption-args (append unoption-args arglst) arglst nil))
	 ((regex "^--([^=]+)=(.*)$" a)
	  (let (opt $1 arg $2)
	    (unless (lookup opt long)
	      (die {Unrecognized option "%s" {%s}} opt a))
	    (unless ((lookup opt long) 1)
	      (die {Option "%s" doesn't take an argument! {%s}} opt a))
	    (when (empty? arg)
	      (die {No argument supplied for option "%s" {%s}} opt a))
	    (eval (first (lookup opt long)))))
	 ((regex "^--([^=]+)$" a)
	  (let (opt $1 arg nil)
	    (unless (lookup opt long)
	      (die {Unrecognized option "%s" {%s}} opt a))
	    (when ((lookup opt long) 1)
	      (unless arglst 
		(die {No argument supplied for option "%s" {%s}} opt a))
	      (setq arg (pop arglst)))
	    (eval (first (lookup opt long)))))
	 ((regex "^-([^-]+)$" a)
	  (let (options $1)
	    (while (not (empty? options))
	      (let (opt (pop options) arg nil)
		(unless (lookup opt short)
		  (die {Unrecognized option "%s" {%s}} opt a))
		(when ((lookup opt short) 1)
		  (if (not (empty? options))
		      (setq arg options options "")
		    (if arglst
			(setq arg (pop arglst))
		      (die {No argument supplied for option "%s" {%s}} opt a))))
		(eval (first (lookup opt short)))))))
	 (true (push a unoption-args -1))
	 )
	)
      )
    unoption-args
    )
  )

(context MAIN)

;; @syntax (getopts <arglist>)
;; @param <arglist> A list of strings, typically a subset of (main-args)
;; @return The list of all command line arguments that were NOT options.
;; After you have set up all the options using 'shortopt' and 'longopt', call 'getopts' to parse the commandline.