;;; parse command line options
;;; this is useful for lisp scripting
;;;
;;; Copyright (C) 2000 by Sam Steingold
;;; This is Free Software, covered by the GNU GPL (v2)
;;; See http://www.gnu.org/copyleft/gpl.html
;;;
;;; $Id: getopt.lisp,v 2.3 2001/11/02 22:31:15 sds Exp $
;;; $Source: /cvsroot/clocc/clocc/src/cllib/getopt.lisp,v $

(eval-when (compile load eval)
  (require :cllib-base (translate-logical-pathname "clocc:src;cllib;base"))
  ;; `kwd'
  (require :cllib-symb (translate-logical-pathname "cllib:symb")))

(in-package :cllib)

(export '(getopt))

(defun parse-object (type string name)
  "Parse the STRING argument named NAME according to the TYPE."
  (case type
    (:char string)
    (:number
     (let ((res (ignore-errors (read-from-string string))))
       (unless (numberp res)
         (error "argument for ~s must be a number, not ~s~%"
                name string))
       res))
    (:boolean (ignore-errors (read-from-string string)))
    (t (error "invalid type (~s) for argument ~s~%" type name))))

;;;###autoload
(defun getopt (arg-alist opt-alist arg-list &key (allow-less t) (allow-more t))
  "Parse the command line arguments.
`arg-alist' and `opt-alist' are alists of (option-name . argument-type),
`argument-type' should be `:char', `:number', or `:boolean',
`arg-list' is a list of options, like (\"-opt1\" \"val1\" ...)
Returns two values -
   a list of non-option arguments,
   and a plist of keyword args;
 or T if there was an error.
When `allow-less' (`allow-more') is non-NIL (default),
allow missing (additional) non-option arguments."
  (do ((ll arg-list (cdr ll)) (aal arg-alist)
       non-opt opts)
      ((null ll)
       (when (and aal (not allow-less))
         (format t "too few non-option arguments (must be at least ~d)~%"
                 (length arg-alist))
         (return t))
       (values (nreverse non-opt) (nreverse opts)))
    (handler-case
        (if (char/= #\- (char (car ll) 0))
            (let ((ar (pop aal)))
              (when (and (null ar) (not allow-more))
                (format
                 t "too many non-option arguments (must be no more than ~d)~%"
                 (length arg-alist))
                (return t))
              (push (parse-object (cdr ar) (car ll) (car ar)) non-opt))
            (let* ((cur (pop ll)) (kw (kwd (nstring-upcase (subseq cur 1))))
                   (opt (assoc kw opt-alist :test #'eq)))
              (unless opt
                (format t "unknown option: ~s~%" cur)
                (return t))
              (push kw opts)
              (push (parse-object (cdr opt) (car ll) cur) opts)))
      (error (co)
        (format t "~a~%" co)
        (return t)))))

(provide :cllib-getopt)
;;; file getopt.lisp ends here
