source: branches/1.2/devel/source/level-1/l1-application.lisp @ 8165

Last change on this file since 8165 was 8165, checked in by gb, 12 years ago

HOUSEKEEPING-LOOP split off (change from trunk.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.0 KB
Line 
1;;;-*-Mode: LISP; Package: CCL -*-
2;;;
3;;;   Copyright (C) 1994-2001 Digitool, Inc
4;;;   Portions Copyright (C) 2001-2004, Clozure Associates
5;;;   This file is part of OpenMCL. 
6;;;
7;;;   OpenMCL is licensed under the terms of the Lisp Lesser GNU Public
8;;;   License , known as the LLGPL and distributed with OpenMCL as the
9;;;   file "LICENSE".  The LLGPL consists of a preamble and the LGPL,
10;;;   which is distributed with OpenMCL as the file "LGPL".  Where these
11;;;   conflict, the preamble takes precedence. 
12;;;
13;;;   OpenMCL is referenced in the preamble as the "LIBRARY."
14;;;
15;;;   The LLGPL is also available online at
16;;;   http://opensource.franz.com/preamble.html
17
18(in-package "CCL")
19
20;; Application classes
21
22(defstruct command-line-argument
23  keyword
24  help-string
25  option-char
26  long-name
27  may-take-operand
28  allow-multiple                        ; option can appear multiple times
29)
30
31(defvar *standard-help-argument*
32  (make-command-line-argument
33   :keyword :help
34   :help-string "this text"
35   :option-char #\h
36   :long-name "help"))
37
38(defvar *standard-version-argument*
39  (make-command-line-argument
40   :keyword :version
41   :help-string "print (LISP-APPLICATION-VERSION) and exit"
42   :option-char #\V
43   :long-name "version"))
44
45(defvar *standard-terminal-encoding-argument*
46  (make-command-line-argument
47   :option-char #\K
48   :long-name "terminal-encoding"
49   :help-string "specify character encoding to use for *TERMINAL-IO*"
50   :may-take-operand t
51   :keyword :terminal-encoding
52   :allow-multiple nil))
53
54(defclass application ()
55    ((command-line-arguments
56      :initform
57      (list *standard-help-argument* *standard-version-argument*))
58     (ui-object :initform nil :initarg :ui-object :accessor application-ui-object)))
59       
60(defclass ui-object ()
61    ())
62
63;;; It's intended that this be specialized ...
64(defmethod ui-object-do-operation ((u ui-object) operation &rest args)
65  (declare (ignore operation args)))
66
67
68(defun %usage-exit (banner exit-status other-args)
69  (with-cstrs ((banner banner)
70               (other-args other-args))
71    (ff-call (%kernel-import target::kernel-import-usage-exit)
72             :address banner
73             :signed-fullword exit-status
74             :address other-args
75             :void)))
76
77(defloadvar *unprocessed-command-line-arguments* ())
78
79;;; Returns four values: error-flag, options-alist, non-option-arguments, unprocessed arguments
80(defmethod parse-application-arguments ((a application))
81  (let* ((cla (slot-value a 'command-line-arguments))
82         (vals (cdr *command-line-argument-list*))
83         (options ())
84         (non-options ())
85         (rest-arg nil))
86    (do* ()
87         ((null vals)
88          (values nil (nreverse options) (nreverse non-options) rest-arg))
89      (let* ((val (pop vals))
90             (val-len (length val))
91             (short-p nil)
92             (option
93              (if (and (>= val-len 2)
94                       (eql (schar val 0) #\-))
95                (if (eql (schar val 1) #\-)
96                  (find val cla
97                        :key #'command-line-argument-long-name
98                        :test #'(lambda (k v) (string= k v :start1 2)))
99                  (progn
100                    (setq short-p t)
101                    (find (schar val 1) cla
102                          :key #'command-line-argument-option-char))))))
103        (if (null option)
104          (if (and (>= val-len 1)
105                   (eql (schar val 0) #\-))
106            (if (and (= val-len 2)
107                     (eql (schar val 1) #\-))
108              (setq rest-arg vals
109                    vals nil)
110              (return (values :unknown-option val nil nil)))
111            (push val non-options))     ;non-option argument
112          ;; We recognized the option.  Is it a duplicate of
113          ;; something already seen?
114          (let* ((key (command-line-argument-keyword option))
115                 (operand nil))
116            (when (and (assoc key options)
117                       (not (command-line-argument-allow-multiple option)))
118              (return (values :duplicate-option val nil)))
119            (when (command-line-argument-may-take-operand option)
120              ;; A short option name can be followed by the operand,
121              ;; without intervening whitespace.
122              (if (and short-p (> val-len 2))
123                (setq operand (subseq val 2))
124                (if vals
125                  (setq operand (pop vals))
126                  (return (values :missing-operand val nil)))))
127            (push (cons key operand) options)))))))
128
129(defmethod summarize-option-syntax ((a application))
130  (flet ((summarize-option (o)
131           (format nil "~8t-~a, --~a : ~a~%"
132                   (command-line-argument-option-char o)
133                   (command-line-argument-long-name o)
134                   (command-line-argument-help-string o))))
135    (format nil "~{~a~}" (mapcar #'summarize-option
136                                 (slot-value a 'command-line-arguments)))))
137
138 
139;;; Process the "help" and "version" options, report parsing errors.
140(defmethod process-application-arguments ((a application) error-flag opts args)
141  (declare (ignore args))
142  (if (null error-flag)
143    (if (assoc :help opts)
144      (%usage-exit "" 0 (summarize-option-syntax a))
145      (if (assoc :version opts)
146        ;; Can't use lisp streams yet.
147        (progn
148          (with-cstrs ((s (format nil "~&~a~&" (application-version-string a))))
149            (fd-write 1 s (%cstrlen s)))
150          (#_ _exit 0))
151        (let* ((encoding (assoc :terminal-encoding opts)))
152          (if encoding
153            (setq *terminal-character-encoding-name*
154                  (if (cdr encoding)
155                    (let* ((*package* (find-package "KEYWORD")))
156                      (ignore-errors (read-from-string (cdr encoding))))))))))
157    (%usage-exit
158     (format nil
159             (case error-flag
160               (:missing-argument "Missing argument to ~a option")
161               (:duplicate-argument "Duplicate ~a option")
162               (:unknown-option "Unknown option: ~a")
163               (t "~a"))
164             opts)
165     #$EX_USAGE
166     (summarize-option-syntax a))))
167               
168
169;;; an example method to base a specialization on
170(defmethod toplevel-function  ((a application) init-file)
171  (declare (ignore init-file))
172  nil )
173
174(defmethod toplevel-function :before ((a application) init-file)
175  (declare (ignore init-file))
176  (multiple-value-bind (error-flag options args rest-arg)
177      (parse-application-arguments a)
178    (setq *unprocessed-command-line-arguments* rest-arg)
179    (process-application-arguments a error-flag options args)
180    (initialize-interactive-streams)))
181
182(defmethod application-version-string ((a application))
183  "Return a string which (arbitrarily) represents the application version.
184Default version returns OpenMCL version info."
185  (lisp-implementation-version))
186
187(defmethod application-ui-operation ((a application) operation &rest args)
188  (let* ((ui-object (application-ui-object a)))
189    (when ui-object
190      (apply #'ui-object-do-operation ui-object operation args))))
191
192
193
194
195(defmethod application-init-file     ((app application)) nil)
196
197
198(defclass lisp-development-system (application) 
199  ((command-line-arguments
200    :initform
201    (list *standard-help-argument*
202          *standard-version-argument*
203          *standard-terminal-encoding-argument*
204          (make-command-line-argument
205           :option-char #\n
206           :long-name "no-init"
207           :keyword :noinit
208           :help-string "suppress loading of init file")
209          (make-command-line-argument
210           :option-char #\e
211           :long-name "eval"
212           :keyword :eval
213           :help-string "evaluate <form> (may need to quote <form> in shell)"
214           :may-take-operand t
215           :allow-multiple t)
216          (make-command-line-argument
217           :option-char #\l
218           :long-name "load"
219           :keyword :load
220           :help-string "load <file>"
221           :may-take-operand t
222           :allow-multiple t)
223          (make-command-line-argument
224           :option-char #\T
225           :long-name "set-lisp-heap-gc-threshold"
226           :help-string "set lisp-heap-gc-threshold to <n>"
227           :keyword :gc-threshold
228           :may-take-operand t
229           :allow-multiple nil)
230          (make-command-line-argument
231           :option-char #\Q
232           :long-name "quiet"
233           :help-string "if --batch, also suppress printing of heralds, prompts"
234           :keyword :quiet
235           :may-take-operand nil
236           :allow-multiple nil)
237          ))
238   (initial-listener-process :initform nil)))
239
240(defparameter *application*
241  (make-instance 'lisp-development-system))
242
243(defvar *load-lisp-init-file* t)
244(defvar *lisp-startup-parameters* ())
245
246(defmethod process-application-arguments ((a lisp-development-system)
247                                          error-flag options args)
248  (declare (ignorable error-flag))
249  (call-next-method)                    ; handle help, errors
250  (if args
251    (%usage-exit (format nil "Unrecognized non-option arguments: ~a" args)
252                 #$EX_USAGE
253                 (summarize-option-syntax a))
254    (progn
255      (setq *load-lisp-init-file* (not (assoc :noinit options))
256            *quiet-flag* (if *batch-flag*
257                           (not (null (assoc :quiet options))))
258            *lisp-startup-parameters*
259            (mapcan #'(lambda (x)
260                        (and (member (car x) '(:load :eval :gc-threshold)) (list x)))
261                    options)))))
262       
263
264(defmethod toplevel-function ((a lisp-development-system) init-file)
265  (let* ((sr (input-stream-shared-resource *terminal-input*)))
266    (with-slots (initial-listener-process) a
267      (setq initial-listener-process
268            (make-mcl-listener-process
269             "listener"
270             *terminal-input*
271             *terminal-output*
272             #'(lambda () (when sr (setf (shared-resource-primary-owner sr)
273                                         *initial-process*)))
274             :initial-function
275             #'(lambda ()
276                 (startup-ccl (and *load-lisp-init-file* init-file))
277                 (listener-function)
278                 nil)
279             :close-streams nil
280             :control-stack-size *initial-listener-default-control-stack-size*
281             :value-stack-size *initial-listener-default-value-stack-size*
282             :temp-stack-size *initial-listener-default-temp-stack-size*
283             :class 'tty-listener
284             :process initial-listener-process))))
285  (%set-toplevel #'housekeeping-loop)
286  (toplevel))
287
288(defun housekeeping-loop ()
289  (with-standard-abort-handling nil 
290    (loop
291      (%nanosleep *periodic-task-seconds* *periodic-task-nanoseconds*)
292      (housekeeping))))
293 
294
295(defmethod application-init-file ((app lisp-development-system))
296  ;; This is the init file loaded before cocoa.
297  #+clozure-common-lisp '("home:ccl-init" "home:openmcl-init") ;; transitional kludge
298  #-clozure-common-lisp "home:openmcl-init")
299
300(defmethod application-error ((a application) condition error-pointer)
301  (declare (ignore condition error-pointer))
302  (quit))
Note: See TracBrowser for help on using the repository browser.