From: pjotr@ludd.luth.se (Peter Sj|str|m)
Newsgroups: comp.sys.amiga.programmer
Subject: Re: Emacs and SAS/C
Date: 17 Dec 92 00:04:09 GMT
Organization: Lule} Tekniska H|gskolas Datorf|rening - Ludd

In <KOPNICKY.92Dec15025822@great-gray.owlnet.rice.edu> kopnicky@great-gray.owlnet.rice.edu (Lyle Warren Kopnicky) writes:

>I have just recently purchased SAS/C 6.0 and I know it allows you to
>use whatever editor you wish.  This can be accomplished by changing a
>tooltype or something.  But don't you also have to change the
>appropriate ARexx scripts for use with the message browser?  I'm using
>gnuemacs.  If anyone had the appropriate scripts, it would be very
>helpful.  Thanks.

	I use elisp code to compile and jump to errors from within emacs.
Here's my elisp code. It's compile.el and compile-ok.el. load compile-ok.el
after you have loaded compile.el into emacs. Hope you don't need more of
my elisp code. I am thinking of uploading a whole package soon, so
hold on. You will have to guess a bit to adapt it to your own disks...

	I usually remap 'compile to C-x RETURN and 'next-error to C-x '.

compile.el:
-----------

;; Slightly altered by Linus 1990 to make it possible to change 
;; buffer names of grep and compile buffers and still have the
;; next-error function run correctly on last compiled buffer
;; Works fine on SAS 6.0 but a bit strange on gcc output - Pjotr

(defvar linus-compile t)

;; Run compiler as inferior of Emacs, and parse its error messages.
;; Copyright (C) 1985, 1986 Free Software Foundation, Inc.

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

(provide 'compile)

;;(defvar compilation-process nil
;;  "Process created by compile command, or nil if none exists now.
;;Note that the process may have been \"deleted\" and still
;;be the value of this variable.")

(defvar compilation-error-list nil
  "List of error message descriptors for visiting erring functions.
Each error descriptor is a list of length two.
Its car is a marker pointing to an error message.
Its cadr is a marker pointing to the text of the line the message is about,
  or nil if that is not interesting.
The value may be t instead of a list;
this means that the buffer of error messages should be reparsed
the next time the list of errors is wanted.")

(defvar compilation-parsing-end nil
  "Position of end of buffer when last error messages parsed.")

(defvar compilation-parsing-continue nil
  "Positions in last buffer you were parsing from.
car is linenumber,
cadr is a marker to that line and
caddr is filename.")

(defvar compilation-error-message nil
  "Message to print when no more matches for compilation-error-regexp are found")

(defvar compilation-error-regexp
  "\\([^ \n]+\\(: *\\|, line \\|(\\)[0-9]+\\)\\|\\([0-9]+ *of *[^ \n]+\\)"
  "Regular expression for filename/linenumber in error in compilation log.")

(defvar compilation-dont-ask nil
  "*Non-nil means \\[compile] requires an argument to let you change
the command.")


(defvar compilation-buffer-name
  "*compilation*"
  "*Name of the buffer created by \\[compile].")

(defvar grep-buffer-name
  "*grep*"
  "*Name of the buffer created by \\[grep].")

(defvar grep-command-default
  "grep -n "
  "*Default command used to do a grep.")

(defvar grep-command
  grep-command-default
  "Last shell command used to do a grep; default for next grep.")

;(defvar compile-command "dmake" ... G|rs i loaddefs)
(setq compile-command "dmake ")

(defvar last-compilation-buffer compilation-buffer-name)

(defun compile (command)
  "Compile the program including the current buffer.  Default: run make.
Runs COMMAND, a shell command, in a separate process asynchronously
with output going to the buffer with the name from compilation-buffer-name.
To change the command give a numeric argument.
You can then use the command next-error, on \\[next-error] to find the
next error message and move to the source code that caused it."
  (interactive (list (cond
		      ((or current-prefix-arg
			   (not compilation-dont-ask))
		       (read-string "Compile command: " compile-command))
		      (t compile-command))))
  (setq compile-command command)
  (compile1 compile-command "No more errors" compilation-buffer-name))

(defun grep (command)
  "Run grep, with user-specified args, and collect output in a buffer.
While grep runs asynchronously, you can use the \\[next-error] command
to find the text that grep hits refer to.
Without argument, prompts with last command.
With argument, prompts with default command."
  (interactive (list (read-string "Grep command: "
				  (cond
				   (current-prefix-arg grep-command-default)
				   (t grep-command)))))
  (setq grep-command command)
  (compile1 (concat command (cond
			     ((string-match "grep" command) " /dev/null")
			     (t "")))
	    "No more grep hits" grep-buffer-name "Grep"))

(defun compile1 (command error-message compilation-buffer-name
			 &optional name-of-mode)
  (save-some-buffers)
  (let ((compilation-process (get-buffer-process compilation-buffer-name)))
    (if compilation-process
	(if (or (not (eq (process-status compilation-process) 'run))
		(yes-or-no-p "A compilation process is running; kill it? "))
	    (condition-case ()
		(let ((comp-proc compilation-process))
		  (interrupt-process comp-proc)
		  (sit-for 1)
		  (delete-process comp-proc))
	      (error nil))
	  (error "Cannot have two compilation processes")))
    (setq compilation-process nil)
    (compilation-forget-errors)
    (setq compilation-error-list t)
    (setq compilation-error-message error-message)
    (if (get-buffer compilation-buffer-name)
	(setq last-compilation-buffer (get-buffer compilation-buffer-name))
      (setq last-compilation-buffer
	    (get-buffer-create compilation-buffer-name)))
    (setq compilation-process
	  (start-process "compilation" last-compilation-buffer
			 shell-file-name
			 "-c" (concat "exec " command)))
    (with-output-to-temp-buffer compilation-buffer-name
      (princ "cd ")
      (princ default-directory)
      (terpri)
      (princ command)
      (terpri))
    (let ((regexp compilation-error-regexp))
      (save-excursion
	(switch-to-buffer last-compilation-buffer)
	(make-local-variable 'compilation-error-regexp)
	(setq compilation-error-regexp regexp)))
    (set-process-sentinel compilation-process 'compilation-sentinel)
    (let* ((thisdir default-directory)
	   (outbuf (process-buffer compilation-process))
	   (outwin (get-buffer-window outbuf)))
      (if (eq outbuf (current-buffer))
	  (goto-char (point-max)))
      (save-excursion
	(set-buffer outbuf)
	(buffer-flush-undo outbuf)
	(let ((start (save-excursion (set-buffer outbuf) (point-min))))
	  (set-window-start outwin start)
	  (or (eq outwin (selected-window))
	      (set-window-point outwin start)))
	(setq default-directory thisdir)
	(fundamental-mode)
	(setq mode-name (or name-of-mode "Compilation"))
	;; Make log buffer's mode line show process state
	(setq mode-line-process '(": %s"))))))

;; Called when compilation process changes state.

(defun compilation-sentinel (proc msg)
  (cond ((null (buffer-name (process-buffer proc)))
	 ;; buffer killed
	 (set-process-buffer proc nil))
	((memq (process-status proc) '(signal exit))
	 (let* ((obuf (current-buffer))
		(omax (point-max))
		(opoint (point)))
	   ;; save-excursion isn't the right thing if
	   ;;  process-buffer is current-buffer
	   (unwind-protect
	       (progn
		 ;; Write something in *compilation* and hack its mode line,
		 (set-buffer (process-buffer proc))
		 (goto-char (point-max))
		 (insert ?\n mode-name " " msg)
		 (forward-char -1)
		 (insert " at "
			 (substring (current-time-string) 0 -5))
		 (forward-char 1)
		 (setq mode-line-process
		       (concat ": "
			       (symbol-name (process-status proc))))
		 ;; If buffer and mode line will show that the process
		 ;; is dead, we can delete it now.  Otherwise it
		 ;; will stay around until M-x list-processes.
		 (delete-process proc))
;;	     (setq compilation-process nil)
	     ;; Force mode line redisplay soon
	     (set-buffer-modified-p (buffer-modified-p)))
	   (if (< opoint omax)
	       (goto-char opoint))
	   (set-buffer obuf)))))

(defun kill-compilation ()
  "Kill the process made by the \\[compile] command."
  (interactive)
  (let ((compilation-process (get-buffer-process compilation-buffer-name)))
    (if compilation-process
	(interrupt-process compilation-process))))

(defun kill-grep ()
  "Kill the process made by the \\[grep] command."
  (interactive)
  (let ((compilation-process (get-buffer-process grep-buffer-name)))
    (if compilation-process
	(interrupt-process compilation-process))))

(defun next-error (&optional argp)
  "Visit next compilation error message and corresponding source code.
This operates on the output from the \\[compile] and \\[grep] commands.
The output from the last started compile or grep is processed unless
you explicitly change the buffer to parse from with 
\\[set-compilation-buffer].
If all preparsed error messages have been processed,
the error message buffer is checked for new ones.
A non-nil argument (prefix arg, if interactive)
means reparse the error message buffer and start at the first error."
  (interactive "P")
  (if (or (eq compilation-error-list t)
	  argp)
      (progn (compilation-forget-errors)
	     (setq compilation-parsing-end 1)))
  (if compilation-error-list
      nil
    (save-excursion
      (switch-to-buffer last-compilation-buffer)
      (set-buffer-modified-p nil)
      (compilation-parse-errors)))
  (let ((next-error (car compilation-error-list))
	(compilation-process (get-buffer-process last-compilation-buffer)))
    (if (null next-error)
	(error (concat compilation-error-message
		       (if (and compilation-process
				(eq (process-status compilation-process)
				    'run))
			   " yet" ""))))
    (setq compilation-error-list (cdr compilation-error-list))
    (if (null (car (cdr next-error)))
	nil
      (switch-to-buffer (marker-buffer (car (cdr next-error))))
      (goto-char (car (cdr next-error)))
      (set-marker (car (cdr next-error)) nil))
    (let* ((pop-up-windows t)
	   (w (display-buffer (marker-buffer (car next-error)))))
      (set-window-point w (car next-error))
      (set-window-start w (car next-error)))
    (set-marker (car next-error) nil)))

;; Set compilation-error-list to nil, and
;; unchain the markers that point to the error messages and their text,
;; so that they no longer slow down gap motion.
;; This would happen anyway at the next garbage collection,
;; but it is better to do it right away.
(defun compilation-forget-errors ()
  (if (eq compilation-error-list t)
      (setq compilation-error-list nil))
  (if compilation-parsing-continue
      (progn (set-marker (car (cdr compilation-parsing-continue)) nil)
	     (setq compilation-parsing-continue nil)))
  (while compilation-error-list
    (let ((next-error (car compilation-error-list)))
      (set-marker (car next-error) nil)
      (if (car (cdr next-error))
	  (set-marker (car (cdr next-error)) nil)))
    (setq compilation-error-list (cdr compilation-error-list))))

(defun compilation-parse-errors ()
  "Parse the current buffer as error messages.
This makes a list of error descriptors, compilation-error-list.
For each source-file, line-number pair in the buffer, the source file
is read in, and the text location is saved in compilation-error-list.
The function next-error, assigned to \\[next-error], takes the next
error off the list and visits its location."
  (setq compilation-error-list nil)
  (message "Parsing error messages...")
  (let (text-buffer
	last-filename last-linenum)
    ;; Don't reparse messages already seen at last parse.
    (goto-char compilation-parsing-end)
    ;; Continue parsing from last parsed buffer if any
    (if compilation-parsing-continue
	(save-excursion
	  (set-buffer
	   (setq text-buffer
		 (marker-buffer (car (cdr compilation-parsing-continue)))))
	  (setq last-filename (car (cdr (cdr compilation-parsing-continue))))
	  (setq last-linenum (car compilation-parsing-continue))
	  (goto-char (car (cdr compilation-parsing-continue)))
	  (set-marker (car (cdr compilation-parsing-continue)) nil)
	  (setq compilation-parsing-continue nil)))
    ;; Don't parse the first two lines as error messages.
    ;; This matters for grep.
    (if (bobp)
	(forward-line 2))
    (while (re-search-forward compilation-error-regexp nil t)
      (let (linenum filename
	    error-marker text-marker)
	;; Extract file name and line number from error message.
	(save-restriction
	  (narrow-to-region (match-beginning 0) (match-end 0))
	  (goto-char (point-max))
	  (skip-chars-backward "[0-9]")
	  ;; If it's a lint message, use the last file(linenum) on the line.
	  ;; Normally we use the first on the line.
	  (if (= (preceding-char) ?\()
	      (progn
		(narrow-to-region (point-min) (1+ (buffer-size)))
		(end-of-line)
		(re-search-backward compilation-error-regexp)
		(skip-chars-backward "^ \t\n")
		(narrow-to-region (point) (match-end 0))
		(goto-char (point-max))
		(skip-chars-backward "[0-9]")))
	  ;; Are we looking at a "filename-first" or "line-number-first" form?
	  (if (looking-at "[0-9]")
	      (progn
		(setq linenum (read (current-buffer)))
		(goto-char (point-min)))
	    ;; Line number at start, file name at end.
	    (progn
	      (goto-char (point-min))
	      (setq linenum (read (current-buffer)))
	      (goto-char (point-max))
	      (skip-chars-backward "^ \t\n")))
	  (setq filename (compilation-grab-filename)))
	;; Locate the erring file and line.
	(if (and (equal filename last-filename)
		 (= linenum last-linenum))
	    nil
	  (beginning-of-line 1)
	  (setq error-marker (point-marker))
	  ;; text-buffer gets the buffer containing this error's file.
	  (if (not (equal filename last-filename))
	      (setq text-buffer
		    (and (file-exists-p (setq last-filename filename))
			 (find-file-noselect filename))
		    last-linenum 0))
	  (if text-buffer
	      ;; Go to that buffer and find the erring line.
	      (save-excursion
		(set-buffer text-buffer)
		(if (zerop last-linenum)
		    (progn
		      (goto-char 1)
		      (setq last-linenum 1)))
		(forward-line (- linenum last-linenum))
		(setq last-linenum linenum)
		(setq text-marker (point-marker))
		(setq compilation-error-list
		      (cons (list error-marker text-marker)
			    compilation-error-list)))))
	(forward-line 1)))
    (if text-buffer
	(setq compilation-parsing-continue
	      (list last-linenum
		    (save-excursion
		      (set-buffer text-buffer)
		      (point-marker))
		    last-filename)))
    (setq compilation-parsing-end (point-max)))
  (message "Parsing error messages...done")
  (setq compilation-error-list (nreverse compilation-error-list)))

(defun compilation-grab-filename ()
  "Return a string which is a filename, starting at point.
Ignore quotes and parentheses around it, as well as trailing colons."
  (if (eq (following-char) ?\")
      (save-restriction
	(narrow-to-region (point)
			  (progn (forward-sexp 1) (point)))
	(goto-char (point-min))
	(read (current-buffer)))
    (buffer-substring (point)
		      (progn
			(skip-chars-forward "^ :,\n\t(")
			(point)))))

(define-key ctl-x-map "`" 'next-error)

(defun set-compilation-buffer (buffer)
 "Sets the buffer to parse with \\[next-error]."
 (interactive "bParsing from buffer:")
 (compilation-forget-errors)
 (setq compilation-parsing-end 1)
 (setq last-compilation-buffer (get-buffer buffer))
 (message "Dir is %s" (save-excursion
			(set-buffer buffer)
			default-directory)))

(load-file "GNU:emacs/lisp/compile-ok.el")



----------------------------------------------------------------------------
compile-ok.el
----------------------------------------------------------------------------

;;; The idea is to depend more on regexps.
;;;
(defvar compilation-all-error-types-regexps
  '(("\\([^\n\t ]*\\):\\([0-9]+\\):.*\n" ((filename 1) (lineno 2))) ;; Common
    ("\\([^\n\t ]*\\) \\([0-9]+\\) \\(Error\\|Warning\\).*\n" ;; SAS 6.0 Amiga
     ((filename 1) (lineno 2))))
  "Regexp variable that contains all possible error types.")


(defun compilation-parse-errors ()
  "Parse the current buffer as error messages.
This makes a list of error descriptors, compilation-error-list.
For each source-file, line-number pair in the buffer, the source file
is read in, and the text location is saved in compilation-error-list.
The function next-error, assigned to \\[next-error], takes the next
error off the list and visits its location."
  (setq compilation-error-list nil)
  (message "Parsing error messages...")
  (let (text-buffer
	last-filename last-linenum)
    ;; Don't reparse messages already seen at last parse.
    (goto-char compilation-parsing-end)
    ;; Continue parsing from last parsed buffer if any
    (if compilation-parsing-continue
	(save-excursion
	  (set-buffer
	   (setq text-buffer
		 (marker-buffer (car (cdr compilation-parsing-continue)))))
	  (setq last-filename (car (cdr (cdr compilation-parsing-continue))))
	  (setq last-linenum (car compilation-parsing-continue))
	  (goto-char (car (cdr compilation-parsing-continue)))
	  (set-marker (car (cdr compilation-parsing-continue)) nil)
	  (setq compilation-parsing-continue nil)))
    ;; Don't parse the first two lines as error messages.
    ;; This matters for grep.
    (if (bobp)
	(forward-line 2))
    (while (not (eobp))
      (let* (linenum filename
	     error-marker text-marker
	     (iamnow (point))
	     (list (sort (mapcar (function (lambda (ele)
				       (goto-char iamnow)
				       (cons
					(if (re-search-forward (car ele) nil t)
					    (cons (match-beginning 0)
						  (match-data))
					  (cons (1+ (point-max))
						(match-data)))
					ele)))
				 compilation-all-error-types-regexps)
			 (function (lambda (ele1 ele2)
				     (< (car (car ele1)) (car (car ele2)))))))
	     (ele (car list))
	     lineno filename)
	(if (and ele (car ele) (<= (car (car ele)) (point-max)))
	    (progn
	      (setq lineno (car (cdr (assoc 'lineno (car (cdr (cdr ele)))))))
	      (setq filename
		    (car (cdr (assoc 'filename (car (cdr (cdr ele)))))))
	      
	      ;; Extract file name and line number from error message.
	      (save-restriction
		(store-match-data (cdr (car ele)))
		(narrow-to-region (match-beginning lineno) (match-end lineno))
		(goto-char (point-min))
		(setq linenum (read (current-buffer)))
		(widen)
		(setq filename (buffer-substring (match-beginning filename)
						 (match-end filename))))
	      ;; Locate the erring file and line.
	      (if (and (equal filename last-filename)
		       (= linenum last-linenum))
		  nil
		(goto-char (match-beginning 0))
		(setq error-marker (point-marker))
		;; text-buffer gets the buffer containing this error's file.
		(if (not (equal filename last-filename))
		    (setq text-buffer
			  (and (file-exists-p (setq last-filename filename))
			       (find-file-noselect filename))
			  last-linenum 0))
		(if text-buffer
		    ;; Go to that buffer and find the erring line.
		    (save-excursion
		      (set-buffer text-buffer)
		      (if (zerop last-linenum)
			  (progn
			    (goto-char 1)
			    (setq last-linenum 1)))
		      (forward-line (- linenum last-linenum))
		      (setq last-linenum linenum)
		      (setq text-marker (point-marker))
		      (setq compilation-error-list
			    (cons (list error-marker text-marker)
				  compilation-error-list)))))
	      (goto-char (match-end 0)))
	  (goto-char (point-max)))))
    (if text-buffer
	(setq compilation-parsing-continue
	      (list last-linenum
		    (save-excursion
		      (set-buffer text-buffer)
		      (point-marker))
		    last-filename)))
    (setq compilation-parsing-end (point-max)))
  (message "Parsing error messages...done")
  (setq compilation-error-list (nreverse compilation-error-list)))
Newsgroups: comp.sys.amiga.programmer