;;; -*-lisp-*-
;;;
-;;; $Id$
-;;;
;;; Zone generator frontend
;;;
;;; (c) 2005 Straylight/Edgeware
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2 of the License, or
;;; (at your option) any later version.
-;;;
+;;;
;;; This program 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 General Public License for more details.
-;;;
+;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, write to the Free Software Foundation,
;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
(defpackage #:zone.frontend
- (:use #:common-lisp #:mdw.optparse #:zone)
+ (:use #:common-lisp #:mdw.sys-base #:optparse #:net #:zone
+ #+(or cmu clisp) #:mop
+ #+sbcl #:sb-mop)
(:export #:main))
(in-package #:zone.frontend)
-(defconstant version "1.0.0")
-
(defvar opt-zones nil
"Which zones to be emitted.")
+(defvar opt-format :bind
+ "Which format to use on output.")
+(defvar opt-debug nil
+ "Whether to emit stack backtraces on error.")
+
+(defun directory-exists-p (name)
+
+ ;; Make a pathname for NAME which has the right form for a directory.
+ (let ((dirpath
+ (let ((path (pathname name)))
+ (if (null (pathname-name path))
+ path
+ (make-pathname :directory
+ (append (or (pathname-directory path)
+ (list :relative))
+ (list (pathname-name path)))
+ :name nil
+ :type nil
+ :defaults path)))))
+
+ ;; Now check that it exists.
+ #+clisp (and (ext:probe-directory dirpath) (truename dirpath))
+ #-clisp (probe-file dirpath)))
-(defvar options nil)
-(defvar usage nil)
-(defun help (arg)
- (declare (ignore arg))
- (show-help *program-name* version usage options)
- (exit 0))
-(defun version (arg)
- (declare (ignore arg))
- (format t "~A, version ~A~%" *program-name* version)
- (exit 0))
-(defun do-usage (&optional (stream *standard-output*))
- (show-usage *program-name* usage stream))
-(defun usage (arg)
- (declare (ignore arg))
- (do-usage)
- (exit 0))
-(setf options
- (options
- "Help options"
- (#\h "help" #'help
- "Show this help message.")
- (#\v "version" #'version
- ("Show the `~A' program's version number." *program-name*))
- (#\u "usage" #'usage
- ("Show a very brief usage summary for `~A'." *program-name*))
+(eval-when (:compile-toplevel :load-toplevel)
+ (defopthandler dir (var arg) ()
+ (let ((path (directory-exists-p arg)))
+ (if (and path
+ (not (pathname-name path)))
+ (setf var path)
+ (option-parse-error "path `~A' doesn't name a directory." arg)))))
- "Output options"
- (#\z "zone" (:arg "NAME") (list opt-zones)
- "Write information about zone NAME.")))
-(setf usage (simple-usage options "ZONEDEF..."))
+(define-program
+ :version "1.0.0" :usage "ZONEDEF..."
+ :help "Generates BIND zone files from Lisp descriptions."
+ :options (options help-options
+ "Parsing options"
+ (#\f "feature" (:arg "KEYWORD")
+ (list *features* 'keyword)
+ "Insert KEYWORD in *features* list.")
+ (#\s "subnet" (:arg "NET")
+ (list zone:*preferred-subnets*)
+ "Designate NET as a preferred subnet.")
+ (#\D "debug" (set opt-debug)
+ "Produce stack backtrace on error.")
+ "Output options"
+ (#\d "directory" (:arg "DIRECTORY")
+ (dir *zone-output-path*)
+ "Write zone and serial files to DIRECTORY.")
+ (#\F "format" (:arg "FORMAT")
+ (keyword opt-format
+ (delete-duplicates
+ (loop for method in
+ (generic-function-methods
+ #'zone:zone-write)
+ for specs =
+ (method-specializers method)
+ if (typep (car specs)
+ 'eql-specializer)
+ collect
+ (eql-specializer-object
+ (car specs)))))
+ "Format to use for output.")
+ (#\z "zone" (:arg "NAME") (list opt-zones)
+ "Write information about zone NAME.")))
(defun main ()
- (with-unix-error-reporting ()
- (let ((seq 54)
- (files nil)
- (op (make-option-parser (cdr *command-line-strings*) options)))
- (unless (option-parse-try
- (loop
- (multiple-value-bind (opt arg) (option-parse-next op)
- (declare (ignore arg))
- (unless opt
- (return))))
- (setf files (option-parse-remainder op))
- (when (zerop (length files))
- (option-parse-error "no files to read")))
- (do-usage *error-output*)
- (exit 1))
- (dolist (f files)
- (let ((*package* (make-package (format nil "zone.scratch-~A"
- (incf seq))
- :use '(#:common-lisp #:zone))))
- (load f :verbose nil :print nil :if-does-not-exist :error)))
- (zone-save opt-zones))))
+ (set-command-line-arguments)
+ (let ((files nil))
+ (flet ((run ()
+ (dolist (f files)
+ (let ((*package* (make-package "ZONE.SCRATCH"
+ :use '(#:common-lisp
+ #:net #:zone))))
+ (load f :verbose nil :print nil :if-does-not-exist :error)
+ (delete-package *package*)))
+ (zone-save opt-zones :format opt-format)))
+ (with-unix-error-reporting ()
+ (unless (option-parse-try
+ (do-options ()
+ (nil (rest)
+ (when (zerop (length rest))
+ (option-parse-error "no files to read"))
+ (setf files rest))))
+ (die-usage)))
+ (if opt-debug
+ (run)
+ (with-unix-error-reporting () (run))))))
;;;----- That's all, folks --------------------------------------------------