1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
6 ;;;; Purpose: Command line option processing a la GNU getopt_long
7 ;;;; Programmer: Kevin M. Rosenberg
8 ;;;; Date Started: Sep 2003
10 ;;;; $Id: package.lisp 7814 2003-09-10 12:56:02Z kevin $
12 ;;;; This file, part of KMRCL, is Copyright (c) 2002 by Kevin M. Rosenberg
14 ;;;; KMRCL users are granted the rights to distribute and use this software
15 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
16 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
17 ;;;; *************************************************************************
22 (defun is-short-option (arg)
23 (and (>= (length arg) 2)
24 (char= #\- (schar arg 0))
25 (char/= #\- (schar arg 1))))
27 (defun is-option-terminator (arg)
28 (and (= 2 (length arg))
29 (char= #\- (schar arg 0))
30 (char= #\- (schar arg 1))))
32 (defun is-long-option (arg)
33 (and (> (length arg) 2)
34 (char= #\- (schar arg 0))
35 (char= #\- (schar arg 1))
36 (char/= #\- (schar arg 3))))
38 (defun arg->base-name (arg option-type)
39 "returns base-name,argument"
40 (let ((start (ecase option-type
43 (name-end (position #\= arg)))
45 (values (subseq arg start name-end)
46 (when name-end (subseq arg (1+ name-end))))))
48 (defun analyze-arg (arg)
49 "Analyzes an argument. Returns option-type,base-name,argument"
50 (let* ((option-type (cond ((is-short-option arg) :short)
51 ((is-long-option arg) :long)
53 (if (or (eq option-type :short) (eq option-type :long))
54 (multiple-value-bind (base arg) (arg->base-name arg option-type)
55 (values option-type base arg))
56 (values :arg arg nil))))
59 (defun match-option (arg options)
60 "Matches an argument to an option. Returns option-list,option-type,base-name,argument"
61 (multiple-value-bind (option-type base-name argument) (analyze-arg arg)
62 (let ((match (find base-name options :key #'car :test #'equal)))
63 (values match option-type base-name argument))))
65 (defun getopt (args options)
66 "Processes a list of arguments and options. Returns filtered argument
67 list and alist of options.
68 opts is a list of option lists. The fields of the list are
69 - NAME name of the long option
70 - HAS-ARG with legal values of :NONE, :REQUIRED, :OPTIONAL
71 - VAL value to return for a option with no arguments
73 (do ((pos args (cdr pos))
78 ((null pos) (values (nreverse out-args) (nreverse out-opts) errors))
81 (push (car pos) out-args))
82 ((is-option-terminator (car pos))
83 (setq finished-options t))
85 (let ((arg (car pos)))
86 (multiple-value-bind (option-list option-type base-name argument)
87 (match-option (car pos) options)
92 (case (second option-list)
94 (push base-name errors))
96 (push (cons base-name argument) out-opts))))
98 (if (and (eq :required (second option-list)) (null (cdr pos)))
99 (push base-name errors)
100 (if (or (is-short-option (second pos))
101 (is-long-option (second pos)))
102 (if (eq :required (second option-list))
103 (push base-name errors)
104 (push (cons base-name (third option-list)) out-args))
106 (push (cons base-name (second pos)) out-opts)
107 (setq pos (cdr pos))))))))
109 (push arg out-args)))))))))