source: trunk/source/level-1/l1-application.lisp @ 10942

Last change on this file since 10942 was 10942, checked in by gz, 11 years ago

Propagate r10938:r10941 (duplicate definition warnings) to trunk

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 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-IMPLEMENTATION-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          (when (cdr encoding)
153            (let* ((encoding-name
154                    (let* ((*package* (find-package "KEYWORD")))
155                      (ignore-errors (read-from-string (cdr encoding))))))
156              (when encoding-name
157                (let* ((character-encoding (lookup-character-encoding encoding-name)))
158                  (when character-encoding
159                    (setq *terminal-character-encoding-name*
160                          (character-encoding-name character-encoding))))))))))
161    (%usage-exit
162     (format nil
163             (case error-flag
164               (:missing-argument "Missing argument to ~a option")
165               (:duplicate-argument "Duplicate ~a option")
166               (:unknown-option "Unknown option: ~a")
167               (t "~a"))
168             opts)
169     #-windows-target #$EX_USAGE #+windows-target #$EXIT_FAILURE
170     (summarize-option-syntax a))))
171               
172
173;;; an example method to base a specialization on
174(defmethod toplevel-function  ((a application) init-file)
175  (declare (ignore init-file))
176  nil )
177
178(defmethod toplevel-function :before ((a application) init-file)
179  (declare (ignore init-file))
180  (multiple-value-bind (error-flag options args rest-arg)
181      (parse-application-arguments a)
182    (setq *unprocessed-command-line-arguments* rest-arg)
183    (process-application-arguments a error-flag options args)
184    (let* ((encoding (lookup-character-encoding *terminal-character-encoding-name*)))
185      (when encoding
186         (set-terminal-encoding (character-encoding-name encoding))))))
187
188(defmethod application-version-string ((a application))
189  "Return a string which (arbitrarily) represents the application version.
190Default version returns OpenMCL version info."
191  (lisp-implementation-version))
192
193(defmethod application-ui-operation ((a application) operation &rest args)
194  (let* ((ui-object (application-ui-object a)))
195    (when ui-object
196      (apply #'ui-object-do-operation ui-object operation args))))
197
198
199
200
201(defmethod application-init-file     ((app application)) nil)
202
203
204(defclass lisp-development-system (application) 
205  ((command-line-arguments
206    :initform
207    (list *standard-help-argument*
208          *standard-version-argument*
209          *standard-terminal-encoding-argument*
210          (make-command-line-argument
211           :option-char #\n
212           :long-name "no-init"
213           :keyword :noinit
214           :help-string "suppress loading of init file")
215          (make-command-line-argument
216           :option-char #\e
217           :long-name "eval"
218           :keyword :eval
219           :help-string "evaluate <form> (may need to quote <form> in shell)"
220           :may-take-operand t
221           :allow-multiple t)
222          (make-command-line-argument
223           :option-char #\l
224           :long-name "load"
225           :keyword :load
226           :help-string "load <file>"
227           :may-take-operand t
228           :allow-multiple t)
229          (make-command-line-argument
230           :option-char #\T
231           :long-name "set-lisp-heap-gc-threshold"
232           :help-string "set lisp-heap-gc-threshold to <n>"
233           :keyword :gc-threshold
234           :may-take-operand t
235           :allow-multiple nil)
236          (make-command-line-argument
237           :option-char #\Q
238           :long-name "quiet"
239           :help-string "if --batch, also suppress printing of heralds, prompts"
240           :keyword :quiet
241           :may-take-operand nil
242           :allow-multiple nil)
243          ))
244   (initial-listener-process :initform nil)))
245
246(defparameter *application*
247  (make-instance 'lisp-development-system))
248
249(defvar *load-lisp-init-file* t)
250(defvar *lisp-startup-parameters* ())
251
252(defmethod process-application-arguments ((a lisp-development-system)
253                                          error-flag options args)
254  (declare (ignorable error-flag))
255  (call-next-method)                    ; handle help, errors
256  (if args
257    (%usage-exit (format nil "Unrecognized non-option arguments: ~a" args)
258                 #-windows-target #$EX_USAGE #+windows-target #$EXIT_FAILURE
259                 (summarize-option-syntax a))
260    (progn
261      (setq *load-lisp-init-file* (not (assoc :noinit options))
262            *quiet-flag* (if *batch-flag*
263                           (not (null (assoc :quiet options))))
264            *lisp-startup-parameters*
265            (mapcan #'(lambda (x)
266                        (and (member (car x) '(:load :eval :gc-threshold)) (list x)))
267                    options)))))
268       
269
270(defmethod toplevel-function ((a lisp-development-system) init-file)
271  (let* ((sr (input-stream-shared-resource *terminal-input*)))
272    (with-slots (initial-listener-process) a
273      (setq initial-listener-process
274            (make-mcl-listener-process
275             "listener"
276             *terminal-input*
277             *terminal-output*
278             #'(lambda () (when sr (setf (shared-resource-primary-owner sr)
279                                         *initial-process*)))
280             :initial-function
281             #'(lambda ()
282                 (startup-ccl (and *load-lisp-init-file* init-file))
283                 (listener-function)
284                 nil)
285             :close-streams nil
286             :control-stack-size *initial-listener-default-control-stack-size*
287             :value-stack-size *initial-listener-default-value-stack-size*
288             :temp-stack-size *initial-listener-default-temp-stack-size*
289             :class 'tty-listener
290             :process initial-listener-process))))
291  (%set-toplevel #'housekeeping-loop)
292  (toplevel))
293
294(defun housekeeping-loop ()
295  (with-standard-abort-handling nil 
296    (loop
297      #+windows-target (#_SleepEx 333 #$true)
298      #-windows-target (%nanosleep *periodic-task-seconds* *periodic-task-nanoseconds*)
299      (housekeeping))))
300 
301
302(defmethod application-init-file ((app lisp-development-system))
303  ;; This is the init file loaded before cocoa.
304  #+unix '("home:ccl-init" "home:\\.ccl-init")
305  #+windows "home:ccl-init")
Note: See TracBrowser for help on using the repository browser.