(in-package "KPML")

;;; File: waiting-generate.lisp
;;; Documentation for use at: http://purl.org/net/kpml/bbox.htm
;;; 
;;;         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;;;          Black box generator capability for KPML
;;;         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;;;
;;;               Windows Lispworks 4.1 + 5.0 ONLY !!!
;;;
;;; Author: John Bateman, Feb 2000.
;;; Updates:              Feb 2002 (more control), Jun 2002 (html added)
;;;                       Dec 2004 (bit more control + KPML4)
;;;                       Jun 2006 (minor control bugs fixed)
;;;                       2007 I/O through sockets
;;;                       2008 use of Racer as KR (Niels)
;;;                       2009 some slightly more complex command options
;;; 
;;; ====================================================
;;; This is a more complex version of the KPML blackbox skeleton
;;; occasionally used elsewhere as an example.
;;;
;;; Defines a simple function that goes into a loop waiting for input
;;; to appear in a particular file.  When the input (as SPL) appears
;;; there, it generates with the currently loading linguistic
;;; resources and places the output in a specified output file.  When
;;; *waiting-verbose* is set, you get a bunch of tracing messages to
;;; see what is going on. Interaction is assumed to be as follows:
;;; (1) some program makes an SPL specification for a sentence and
;;; writes it to the input file; (2) the black box generator sees this
;;; and writes the generated string to the output file; (3) some program
;;; (possibly the same as in (1)) detects that a string has appeared
;;; in the output file and does something with it; (4) go back to (1).
;;; In order to stop the entire process, the single item ":stop" must
;;; be placed in the input file.
;;;
;;; One easy way of using this is to load up KPML, load some linguistic
;;; resources for generating, and then to make a standalone executable
;;; image. The application function for starting up the image should
;;; be the WAITING-GENERATE function defined below. A trivial delivery
;;; call example for Lispworks could be:
;;; 
;;;            (lispworks::deliver 
;;;                  'kpml::waiting-generate "kpml4-bbox" 0
;;;                  :multiprocessing T :keep-clos T :keep-eval T)
;;; 
;;; Better would be something more space-efficient, etc. but this would
;;; minimally do the job.
;;;
;;; Update: Feb 2002. A little more functionality is added here
;;;                   to make the resulting blackbox a bit more useful.
;;;                   In addition, we generate only in strict monolingual
;;;                   mode for faster generation. This shows how the
;;;                   the blackbox might be extended for particular
;;;                   clients. We also add an attempt at supporting DDE
;;;                   interprocess communication and allow the KPML
;;;                   configuration file to be reloaded on demand. This
;;;                   latter actually allows us to change any KPML
;;;                   internal flags during running of the blackbox.
;;;         Dec 2004. Two new control line flags added: (i) :domain, 
;;;                   for just adding domain files and (ii) :text 
;;;                   for electing to process all the SPLs in a file
;;;                   one after the other rather than just the first.
;;;                   Also an in-file command :vars for showing the current
;;;                   state of all control variables.
;;;         Dec 2007. Socket-based communication added, plus update
;;;                   for Lispworks 5.
;;;         Aug 2008: Added support for interaction with reasoner racer
;;;                   (Niels)
;;;         Aug 2009: John: moved stuff specifically to do with Racer
;;;                  (apart from harmless globals) out of this file into the
;;;                  Racer/kpml-kr-interface files.
;;;
;;; ==================================================


  ;; ----------------------------------------------------------------------
  ;; Determine where the input comes from, where the output goes, and
  ;; a few other aspects of the generator's behaviour...
  ;; ----------------------------------------------------------------------
  ;;
(defvar *spls-input-file* "C:\\tmp\\spls-in.lisp")
(defvar *kpml-output-file* "C:\\tmp\\kpml-out.txt")
(defvar *config-init-file* nil)

;; 4.1+ unclear what the following was ever for: replaced here by
;;         direct changes of curlan...
(defvar *waiting-language* :english) 
;; change this if other language resources
;; are loaded 

(defvar *waiting-verbose* nil)  ;; tracing on/off
(defvar *html-mode* nil)        ;; output responds to HTML annotations in input

(defvar *poling-frequency* 2) ;; should be more than zero if any other
                              ;; processes are going to get a look-in.

(defvar *dde-activated* nil)  ;; when true, input and output goes via DDE
                              ;; (not yet implemented: low priority)

(defvar *text-mode* nil)      ;; when true, process all the SPLs in a file
(defvar *domain-file* nil)    ;; when set, loads the domain file identified
(defvar *port-number* nil)  ;; when set, initiates socket driven interaction
(defvar *generated-text* nil) ;; collects generated results for output

(setf *editing-protocol* nil) ;; no point in allowing this for a blackbox

;; niels: additional parameters for racer interaction

(defvar *mirrorlist* nil)
(defvar *ontolist* nil)
(defvar *racer-port* nil)
(defvar *use-racer* nil)
(defvar *annotationlist* nil)
(defvar *language* nil)

  ;; ----------------------------------------------------------------------
  ;; The main looping function itself...
  ;; ----------------------------------------------------------------------
  ;;
(defun WAITING-GENERATE ()
  (let ((socket-stream nil)
	(input-data-list nil)
	)
    (setf example-runner-flag T)
    (initialise-server-globals)

    ;; first time round, check the command line for flags and commands.
    ;; this may return some
    ;; commands to be done...
    (setf input-data-list (process-any-command-arguments))

    ;; JB:Aug09: initialise KPML ready for generation...
    ;;                  by the time we get here, we should have loaded resources
    ;;                  that are set on the command line... If the server is
    ;;                  made to load any more resources after this, then
    ;;                  the user will have to make sure that initialisations
    ;;                  are redone as appropriate...
    (kpml::do-all-kpml-initializations)
    (kpml::setup-spl)

    (print-welcome-lines *trace-output*)
    
    (cond (*port-number*
	   ;;
	   ;; generation is running over the socket, no need
	   ;; to do any more here...
	   ;;
	   (format *trace-output* 
		   "~%;;; Generation over socket at port: ~S" 
		   *port-number*)
	   nil
	   )
	  (t
	   (loop for input-data-list = (get-input-from-file)
		 do
		 (sleep *poling-frequency*)
		 ;; ----------------------------------------
		 ;; more tracing...
		 ;; ----------------------------------------
		 (when *waiting-verbose*
		   (format *trace-output* 
			   "~%;;; Following read: ~S" input-data-list))
		 ;; JB: 2015-05-11
		 ;; The macro is folded in here as it was being expanded under LW4
		 ;; with the wrong package ... leading to input-data-list being
		 ;; unbound... Should correct and put back at some stage...
		 ;; (process-input-line)
		 (when input-data-list
     
     ;; get rid of the input file (if still there) and get ready
     ;; to accumulate generated sentences, etc.
     ;;
     (when (and (not *port-number*)
		(probe-file *spls-input-file*))
       (delete-file *spls-input-file*))
     (setf *generated-text* nil)
     
     ;; ------------------------------
     ;; run through the interaction
     ;; possibilities... (now can be a list of SPLs (not commands!))
     ;; ------------------------------
     (loop for input-data in input-data-list
       do
       (case input-data
	     ;; see if we are done...
	     (:stop 
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Stop found. Exiting..."))
	      (when *port-number* (close socket-stream))
	      (return-from WAITING-GENERATE T))

	     ;; toggle verbosity...
	     (:verbose
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling verbosity..."))
	      (setf *waiting-verbose* (not *waiting-verbose*)))

	     ;; toggle strict monolinguality... use this only when
	     ;; you have resources that do not involve any language
	     ;; conditionalization. Generation is then faster.
	     (:mono
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling strict monolinguality..."))
	      (setf *rigidly-monolingual* (not *rigidly-monolingual*)))
	      
	     ;; toggle html output mode...
	     ((:html :xml)
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling HTML/XML output mode..."))
	      (setf *html-mode* (not *html-mode*)))

	     ;; toggle html output mode...
	     (:text
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling text output mode..."))
	      (setf *text-mode* (not *text-mode*)))

	     ;; toggle dde interaction for input/output...
	     (:dde
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling dde io..."))
	      (setf *dde-activated* (not *dde-activated*)))

	     ;; see if we are to reload the configuration file...
	     (:configure
	      (kpml::load-kpml-configuration-file))

	     ;; redo the initializations on demand...
	     (:init
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Reinitializing..."))
	      (kpml::do-all-kpml-initializations))

	     (:language
	      (format *trace-output* "~%;;; Current language is '~S'" kpml::curlan))

	     ;; reports which version of the blackbox is running...
	     (:version
	      (kpml::describe-kpml))

	     ;; load lexicon of selected language anew...
	     (:lexicon
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Loading lexicons..."))
	      (kpml::load-lexicons kpml::curlan :clear nil))

	     (:domain
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Loading domains..."))
	      (kpml::load-domains kpml::curlan :clear nil))
	      
	     (:vars
	      (report-var-values *trace-output*))

	     (:break-for-debug
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; breaking for debug...")
		(break)))

	     (t
	      ;; if the input is a single nonrecognized keyword, it
	      ;; may be a language resource that we should
	      ;; attempt to load... e.g., :english, :french, etc.
	      ;; check for this and do it if possible, otherwise
	      ;; ignore...
	      (cond ((keywordp input-data)
		     (cond ((language-variety-loadable-p input-data)
			    (when *waiting-verbose*  ;; tracing
			      (format *trace-output* 
				      "~%;;; Attempting reload of: ~S" input-data))
			    
			    (setf kpml::curlan input-data)
			    (when *rigidly-monolingual*
			      (setf kpml::all_languages (list input-data)))
		     
			    (kpml::load-linguistic-resources input-data)
			    (kpml::do-all-kpml-initializations))
			   (t
			    (format *trace-output* 
				    "~%;;; Unrecognised command: ~S" input-data))))
		    (t 
		     ;; if none of the commands, then may be a complex command 
		     (when *waiting-verbose*  ;; tracing
		       (format *trace-output* "~%;;; Something found: ~S" input-data)
		       (cond  ((listp input-data)
			       (case (first input-data)
				     (:setlanguage
				      (setf kpml::curlan (second input-data))
				      (format *trace-output* 
					      "~%;;; Current language changed to: ~S" kpml::curlan))
				     (t

				      ;; nothing left to do but try and generate it! ...
				      
				      ;; ----------------------------------------
				      ;; Generation...
				      ;; ----------------------------------------
				      (let ((result
					     (unwind-protect
						 ;; ----------------------------------------
						 ;; Again be very cautious about anything 
						 ;; blowing up during generation...
						 ;; ----------------------------------------
						 (multiple-value-bind
						  (value error)
						  (ignore-errors
						   (let ((result :nothing))
						     ;; ----------------------------------------
						     ;; if we needed user interaction during generation
						     ;; then the SPL was not specific enough, report that
						     ;; too...
						     ;; ----------------------------------------
						     (catch 'erunner
						       (setf result 
							     (if *html-mode*
								 (html-say input-data :language kpml::curlan)
							       (say input-data :language kpml::curlan))))
						     (if (equal result :nothing)
							 (format 
							  *trace-output* "~%;;; Generation required user interaction."))
						     result))
						  (progn
						    (when error
						      ;; ----------------------------------------
						      ;; If we get here, then generation failed...
						      ;; ----------------------------------------
						      (setf value nil)
						      (format 
						       *trace-output* "~%;;; Generation failure.")
						      (when *waiting-verbose*
							(format *trace-output* "~%;;; Error: ~S" error))
						      (format 
						       *trace-output* "~%;;; SPL was ~S" input-data))

						    ;; ----------------------------------------
						    ;; Otherwise we were fine
						    ;; ----------------------------------------
						    ;;
						    value)))))
		       
					;; ----------------------------------------
					;; Collect the generated result (if anything)
					;; to the generated-text record
					;; ----------------------------------------		       
					(when (and result (not (equal result :nothing)))
					  (when *waiting-verbose*  ;; tracing
					    (format 
					     *trace-output* "~%;;; Something generated: ~S" result))
					  (setf *generated-text* (cons result *generated-text*)))
		       
					)
				      )))
			      (t (format *trace-output* 
				    "~%;;; Unrecognised command: ~S" input-data))))
		     ))))))

		 (with-open-file (stream *kpml-output-file*
					 :direction :output
					 :if-exists :new-version)
				 (format stream "~{~A ~}" 
					 (reverse *generated-text*)))))
	  )))


  ;; ----------------------------------------------------------------------
  ;; Main line processing ...
  ;; ----------------------------------------------------------------------
  ;;
(defmacro PROCESS-INPUT-LINE ()
  ;; ----------------------------------------
  ;; when something found, check if it is an action, if not
  ;; actually do some generating... 
  ;; ----------------------------------------
  '(when input-data-list
     
     ;; get rid of the input file (if still there) and get ready
     ;; to accumulate generated sentences, etc.
     ;;
     (when (and (not *port-number*)
		(probe-file *spls-input-file*))
       (delete-file *spls-input-file*))
     (setf *generated-text* nil)
     
     ;; ------------------------------
     ;; run through the interaction
     ;; possibilities... (now can be a list of SPLs (not commands!))
     ;; ------------------------------
     (loop for input-data in input-data-list
       do
       (case input-data
	     ;; see if we are done...
	     (:stop 
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Stop found. Exiting..."))
	      (when *port-number* (close socket-stream))
	      (return-from WAITING-GENERATE T))

	     ;; toggle verbosity...
	     (:verbose
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling verbosity..."))
	      (setf *waiting-verbose* (not *waiting-verbose*)))

	     ;; toggle strict monolinguality... use this only when
	     ;; you have resources that do not involve any language
	     ;; conditionalization. Generation is then faster.
	     (:mono
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling strict monolinguality..."))
	      (setf *rigidly-monolingual* (not *rigidly-monolingual*)))
	      
	     ;; toggle html output mode...
	     ((:html :xml)
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling HTML/XML output mode..."))
	      (setf *html-mode* (not *html-mode*)))

	     ;; toggle html output mode...
	     (:text
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling text output mode..."))
	      (setf *text-mode* (not *text-mode*)))

	     ;; toggle dde interaction for input/output...
	     (:dde
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Toggling dde io..."))
	      (setf *dde-activated* (not *dde-activated*)))

	     ;; see if we are to reload the configuration file...
	     (:configure
	      (kpml::load-kpml-configuration-file))

	     ;; redo the initializations on demand...
	     (:init
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Reinitializing..."))
	      (kpml::do-all-kpml-initializations))

	     (:language
	      (format *trace-output* "~%;;; Current language is '~S'" kpml::curlan))

	     ;; reports which version of the blackbox is running...
	     (:version
	      (kpml::describe-kpml))

	     ;; load lexicon of selected language anew...
	     (:lexicon
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Loading lexicons..."))
	      (kpml::load-lexicons kpml::curlan :clear nil))

	     (:domain
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; Loading domains..."))
	      (kpml::load-domains kpml::curlan :clear nil))
	      
	     (:vars
	      (report-var-values *trace-output*))

	     (:break-for-debug
	      (when *waiting-verbose*
		(format *trace-output* "~%;;; breaking for debug...")
		(break)))

	     (t
	      ;; if the input is a single nonrecognized keyword, it
	      ;; may be a language resource that we should
	      ;; attempt to load... e.g., :english, :french, etc.
	      ;; check for this and do it if possible, otherwise
	      ;; ignore...
	      (cond ((keywordp input-data)
		     (cond ((language-variety-loadable-p input-data)
			    (when *waiting-verbose*  ;; tracing
			      (format *trace-output* 
				      "~%;;; Attempting reload of: ~S" input-data))
			    
			    (setf kpml::curlan input-data)
			    (when *rigidly-monolingual*
			      (setf kpml::all_languages (list input-data)))
		     
			    (kpml::load-linguistic-resources input-data)
			    (kpml::do-all-kpml-initializations))
			   (t
			    (format *trace-output* 
				    "~%;;; Unrecognised command: ~S" input-data))))
		    (t 
		     ;; if none of the commands, then may be a complex command 
		     (when *waiting-verbose*  ;; tracing
		       (format *trace-output* "~%;;; Something found: ~S" input-data))
		     (cond  ((listp input-data)
			       (case (first input-data)
				     (:setlanguage
				      (setf kpml::curlan (second input-data))
				      (format *trace-output* 
					      "~%;;; Current language changed to: ~S" kpml::curlan))
				     (t

				      ;; nothing left to do but try and generate it! ...
				      
				      ;; ----------------------------------------
				      ;; Generation...
				      ;; ----------------------------------------
				      (let ((result
					     (unwind-protect
						 ;; ----------------------------------------
						 ;; Again be very cautious about anything 
						 ;; blowing up during generation...
						 ;; ----------------------------------------
						 (multiple-value-bind
						  (value error)
						  (ignore-errors
						   (let ((result :nothing))
						     ;; ----------------------------------------
						     ;; if we needed user interaction during generation
						     ;; then the SPL was not specific enough, report that
						     ;; too...
						     ;; ----------------------------------------
						     (catch 'erunner
						       (setf result 
							     (if *html-mode*
								 (html-say input-data :language kpml::curlan)
							       (say input-data :language kpml::curlan))))
						     (if (equal result :nothing)
							 (format 
							  *trace-output* "~%;;; Generation required user interaction."))
						     result))
						  (progn
						    (when error
						      ;; ----------------------------------------
						      ;; If we get here, then generation failed...
						      ;; ----------------------------------------
						      (setf value nil)
						      (format 
						       *trace-output* "~%;;; Generation failure.")
						      (when *waiting-verbose*
							(format *trace-output* "~%;;; Error: ~S" error))
						      (format 
						       *trace-output* "~%;;; SPL was ~S" input-data))

						    ;; ----------------------------------------
						    ;; Otherwise we were fine
						    ;; ----------------------------------------
						    ;;
						    value)))))
		       
					;; ----------------------------------------
					;; Collect the generated result (if anything)
					;; to the generated-text record
					;; ----------------------------------------		       
					(when (and result (not (equal result :nothing)))
					  (when *waiting-verbose*  ;; tracing
					    (format 
					     *trace-output* "~%;;; Something generated: ~S" result))
					  (setf *generated-text* (cons result *generated-text*)))
		       
					)
				      )))
			      (t (format *trace-output* 
				    "~%;;; Unrecognised command: ~S" input-data)))
		     )))))))

  ;; ----------------------------------------------------------------------
  ;; Get the input data from the named file...
  ;; ----------------------------------------------------------------------

(defmacro SAFE-READ (listp)
  ;; This just wraps enough condition handlers and error squashers
  ;; around the reading phase that it does not matter what gets
  ;; passed in or fails to get passed in.
  ;;
  `(unwind-protect
       (multiple-value-bind
	   (value error)
	   (ignore-errors 
	     (let ((*package* (find-package "KPML")))
	       (read stream :eof-error-p nil)))
	 (progn
	   (when error
	     (setf value nil)
	     (when *waiting-verbose*
	       (format *trace-output* "~%;;; SPL read error: ~S." error)))
	   ,(if listp '(list value)
	      'value)))))

(defun GET-INPUT-FROM-FILE ()
  (with-open-file (stream *spls-input-file* :direction :input
			  :if-does-not-exist nil)
     (when (streamp stream)
       (when *waiting-verbose*
	 ;; ----------------------------------------
	 ;; provide the tracing information for debugging and
	 ;; seeing what is going on...
	 ;; ----------------------------------------
	 (format *trace-output* 
		 "~%;;; SPL input file found, loading..."))
	      
       (let ((input-data-list
	      ;; ----------------------------------------
	      ;; be nice and cautious about failures to 
	      ;; read some SPL, i.e., including
	      ;; garbage input...
	      ;; ----------------------------------------
	      (if *text-mode*
		  (loop for s = (safe-read nil) while s collect s)
		(safe-read T)
		)))
	 input-data-list))))

  ;; ---------------------------------------------------------------------
  ;; Following not yet supported. 

  ;; ----------------------------------------------------------------------
  ;; Send the generated result via a Windows DDE conversation...
  ;; ----------------------------------------------------------------------

(defun GET-INPUT-FROM-DDE-CONVERSATION ()
  (format *trace-output* "~%;;; DDE interaction not yet supported.")
  nil)

  ;; ----------------------------------------------------------------------
  ;; Send the generated result via a Windows DDE conversation...
  ;; ----------------------------------------------------------------------

(defun SEND-GENERATED-OUTPUT-VIA-DDE (result)
  nil)



  ;; ----------------------------------------------------------------------
  ;; Make the initial settings of the various flags settable by command
  ;; line parameters. All are optional. A command may be returned for
  ;; initial performance via the :first flag. Note that this must
  ;; be returned as a keyword symbol to be recognised appropriately
  ;; in the course of normal processing.
  ;;
  ;; parameters are:
  ;;   :in       sets the default SPL input file
  ;;   :out      sets the default generated result output file
  ;;   :first    sets the first command to be performed
  ;;   :poling   sets poling frequency
  ;;   :mono     if present sets the strictly monolingual mode
  ;;   :verbose  if present sets verbosity on
  ;;   :html     if present puts us in HTML IO mode
  ;;   :text     if present always processes all the SPLs in the input file
  ;;   :with-domain  loads the identified file as a domain model
  ;;   :with-lexicon  loads the identified file as a lexicon file
  ;;   :port     if present, goes for socket-based communication
  ;;
  ;; Plus the following added since the last public documentation:
  ;;
  ;;   :resource-root
  ;;   :racer-port
  ;;   :use-racer
  ;;   :mirror
  ;;   :current-language
  ;;   :annotations
  ;;   :load-ontologies
  ;; ----------------------------------------------------------------------

(defun PROCESS-ANY-COMMAND-ARGUMENTS ()
  (let* ((command-line (get-command-line))
	 (position nil)
	 (length (1- (length command-line)))
	 (first nil))
    (when (>= length 0)
      (when (member ":verbose" command-line :test #'string=)
	(setf *waiting-verbose* T))
      (when *waiting-verbose*
	(format *trace-output* "~%;;; Command line was: ~S" command-line))
      (when (member ":mono" command-line :test #'string=)
	(setf *rigidly-monolingual* T))
      (when (setf position (position ":poling" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (let ((pf (read-from-string (elt command-line (1+ position)))))
	  (setf *poling-frequency* 
		(if (and (integerp pf)(> pf 0))
		    pf
		  1)))))
      (when (member ":html" command-line :test #'string=)
	(setf *html-mode* T))
      (when (member ":xml" command-line :test #'string=)
	(setf *html-mode* T))
      (when (member ":text" command-line :test #'string=)
	(setf *text-mode* T))
      (when (setf position (position ":in" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf *spls-input-file* (elt command-line (1+ position)))))
      (when (setf position (position ":out" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf *kpml-output-file* (elt command-line (1+ position)))))

      ;;set port number for interaction with user
      (when (setf position (position ":port" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf *port-number* 
		(read-from-string (elt command-line (1+ position))))
	  (if (numberp *port-number*)
	      (cl-user::start-kpml-server *port-number*)
	    (progn
	      (format *trace-output* 
		      "~%;;; Bad port number given: ~S!" *port-number*)
	      (setf *port-number* nil)))))

      ;; JB: added for completeness... (Aug09)
      (when (setf position (position ":resource-root" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf *root-of-resources* (pathname (nth (1+ position) command-line)))))

      ;; the following requires additional action, as we immediately
      ;; reconfigure according to the argument given... this can therefore
      ;; override setting of the resource-root in the command line...
      (when (setf position 
		  (position ":confile" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf *config-init-file* (elt command-line (1+ position)))
	  (when (probe-file *config-init-file*)
	    (process-configuration-file *config-init-file*))))

      ;; similarly for this option, but we just use the default
      ;; configuration file mechanisms...
      (when (setf position 
		  (position ":configure" command-line :test #'string=))
	(kpml::load-kpml-configuration-file))

      ;;set port number for interaction with racer (Niels)
      ;; JB: cleaned up and spurious global removed...
      (when (setf position (position ":racer-port" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf *racer-port* (read-from-string (elt command-line (1+ position))))
	  (setf *use-racer* t)  ;;John: specifying a port I guess entails that you want to use racer...
	  (unless (numberp *racer-port*)
	    (format *trace-output* 
		    "~%;;; Bad port number given for racer: ~S!" *racer-port*)
	    (setf *racer-port* nil))))

      (when (setf position (position ":current-language" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf kpml::curlan 
		(let ((command-line-param (nth (1+ position) command-line)))
		  (intern
		   (string-upcase
		    (if (char= (char command-line-param 0) #\:) 
			(subseq command-line-param 1)
		      command-line-param))
		   "KEYWORD")))
	  (pushnew kpml::curlan all_languages)
	  ))

      (when (setf position (position ":load-language" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf *language* (nth (1+ position) command-line))
	  (load-language *language*)))

      (when (setf position (position ":with-lexicon" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf *domain-file* (elt command-line (1+ position)))
          (if (probe-file *domain-file*)
	      (load *domain-file*)
	    (format *trace-output* 
		    "~%;;; Requested lexicon file: ~S did not exist!" 
		    *domain-file*))))

      (when (setf position (position ":load-ontologies" command-line :test #'string=))
	(setf *ontolist* (collect-params command-line position))
	)

      (when (setf position (position ":use-racer" command-line :test #'string=))
	(setf *use-racer* t))

      (when (setf position (position ":mirror" command-line :test #'string=))
	(setf *mirrorlist* (collect-params command-line position))
	)

      (when (setf position (position ":annotations" command-line :test #'string=))
	(setf *annotationlist* (collect-params command-line position))
	)

      (when (setf position (position ":with-domain" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf *domain-file* (elt command-line (1+ position)))
          (if (probe-file *domain-file*)
	      (load *domain-file*)
	    (format *trace-output* 
		    "~%;;; Requested domain file: ~S did not exist!" 
		    *domain-file*))))

      (when (setf position (position ":first" command-line :test #'string=))
	(unless (> (1+ position) length)
	  (setf first (elt command-line (1+ position)))
	  (if (stringp first)
	      (setf first 
		    (intern
		     (string-upcase
		      (if (char= (char first 0) #\:) 
			  (subseq first 1)
			first))
		     "KEYWORD"))
	    (setf first nil))
	  )))
    (list first)))

;;; ----------------------------------------------------------------------
;;; Utilities/extras...
;;; ......................................................................

(defun REPORT-VAR-VALUES (stream)
  (format stream "~{~%;;;    ~{~30A = ~S~}~}"
	  `(("SPLs input file" ,*spls-input-file*)
	    ("Generated output file" ,*kpml-output-file*)
	    ("Configuration file" ,*config-init-file*)
	    ("Current language" ,kpml::curlan)
	    ("HTML mode" ,*html-mode*)
	    ("Poling frequency" ,*poling-frequency*)
	    ("DDE activated (should be NIL)" ,*dde-activated*))))

(defun PRINT-WELCOME-LINES (stream)
  (format stream "~%;;; KPML Version 4.1+")
  (format stream "~%;;;       Multilingual Natural Language Generation System")
  (format stream "~%;;;       for systemic-functional grammars")
  (format stream "~2%;;; Copyright (C) 1999-2009, University of Bremen.")
  (format stream "~%;;; KPML comes with no warranty and use is at your own risk.")
  (format stream "~%;;; Commercial use is prohibited; contact the author for licensing.")
  (format stream "~2%;;; This version of KPML is running with:")
  (format stream "~%;;;      Lispworks (www.lispworks.com), ")
  (if *use-racer*
      (format stream " the KR system Racer")
    (format stream " the KR system Loom (www.isi.edu), "))
  (format stream "~%;;;      and is file- or socket-based for input and output. ")
  (kpml::describe-kpml)
  (format stream "~%;;; Loaded grammar details (if any): ~{~%;;;    ~A~}"
	  (third 
	   (assoc kpml::curlan kpml::*language-description-profiles*)))
  (format stream "~%;;;~%;;; [~A processing ~%;;;        ~A -> ~A ...]" 
	  (time-stamp) *spls-input-file* *kpml-output-file*)
  )

;;; ----------------------------------------------------------------------
;;; Allow each instantiation of the server to attempt to
;;; establish its own environment sensibly...
;;;
(defun INITIALISE-SERVER-GLOBALS ()
  (setf *root-of-resources* 
	(getenv "KPMLRESOURCEBASE"
		(merge-pathnames "Resources/" 
				 (cl-user::kpml-parent-directory cl-user::*root-of-kpml*))))
  )


;;; ----------------------------------------------------------------------
;;; JB[Aug09]: these appear to have been added for command line
;;;                   parameters of the form :keyword para*
;;;                   Made more robust and cleaned up here....
;;;
;;start-index: index of the keyword belonging to the params
(defun COLLECT-PARAMS (param-list start-index)
  (loop for x in (subseq param-list (1+ start-index))
	until (stringkeywordp x)
	unless (string= x "") collect x))
  
(defun STRINGKEYWORDP (word)
  (and 
   (> (length word) 1)
   (eq (char word 0) #\:)
  ))

(defun load-language (lang)
  (load-linguistic-resources (intern lang "KEYWORD")))



