I'm trying to upgrade a protocol, switching from HTTP 1.1 to WebSockets. I've tried to use the usocket. My code so far follows (and is available as a GitHub gist). After the handshake reading, functions return NIL or unexpected EOF error.
;; Define parameter sock for usocket stream
;; echo.websocket.org is a site for testing websockets
(defparameter sock (usocket:socket-connect "echo.websocket.org" 80))
;; Output confirms WebSocket protocol handshake as this implemented in browsers
(format (usocket:socket-stream sock) "~A~%~A~%~A~%~A~%~A~A~%~A~%~A~%~%"
"GET /?encoding=text HTTP/1.1"
"Connection: Upgrade"
"Host: echo.websocket.org"
"Origin: http://www.websocket.org"
"Sec-WebSocket-Key: " (generate-websocket-key)
"Sec-WebSocket-Version: 13"
"Upgrade: websocket")
;; Write output to stream
(force-output (usocket:socket-stream sock))
;; Returns NIL
(do ((line
(read-line (usocket:socket-stream sock) nil)
(read-line (usocket:socket-stream sock) nil)))
((not line))
(format t "~A" line))
;; Returns error: unexpected EOF
(read-line (usocket:socket-stream sock))
;; Returns NIL
(listen (usocket:socket-stream sock))
~% in a FORMAT statement is not portable for the purpose of HTTP protocols. It outputs the newline character #\newline. A newline depends on the platform the code runs on: cr (old Macs, Lisp Machines), crlf (Windows) or lf (Unix). Thus if you write a newline character to a stream, the output depends on the platform you are running on (or what the Lisp system thinks it should do). Windows has the same line end convention as HTTP.
Note:
on a Unix system usually #\newline is the same as #\linefeed. A single character.
on a Windows system often #\newline is the same as the sequence #\return #\linefeed. Two characters. Some Lisp systems might ignore this and use Unix conventions.
HTTP uses crlf. To reliably write crlf, you have to write the characters #\return and #\linefeed: (format stream "~a~a" #\return #\linefeed).
(defun write-crlf (stream)
(write-char #\return stream)
(write-char #\linefeed stream))
Some servers might be dumb enough to read input which does not follow the standard HTTP crlf conventions, though.
There might be ways to specify the line ending convention when opening a stream. usocket does not provide such a functionality, though.
I would also use finish-output (waits for completion) and not force-output (does NOT wait for completion of IO operations).
Related
Editing a Bash script I want to assign a filename to a variable.
E.g. inputfile=foo.txt
With std. settings I can't complete the filename without first inserting a space after the '='.
Is there any solution to this?
First of all, comint-dynamic-complete has been obsolete since Emacs 24.1. The replacement function is completion-at-point.
Now, if you starting looking at what completion-at-point actually does in a shell script buffer, you'll eventually end up in comint anyway. In particular, the function comint--match-partial-filename looks promising for an explanation of the behavior you described.
If I read that correctly, the problem here is that "=" is considered a valid part of a filename, at least on POSIX-like systems (see variable comint-file-name-chars). So, the completion mechanism is trying to complete the filename "inputfile=/..." which it can obviously not find.
If you never use a "=" in your filenames (or you use it so rarely that the working completion outweighs other downsides), you may want to consider doing something like (setq comint-file-name-chars "[]~/A-Za-z0-9+#:_.$#%,{}-") in the shell script mode hook (if you are on a POSIX system; on Windows it would look slightly different).
Hope that helps.
You can use bash-completion assuming your not on windows. It just requires a slight modification to work in sh-mode since it uses a comint function to determine the current completion candidate.
I like this because, in addition to completing filenames there, it also will give you all the nice readline completion like command line switches, etc. Here is an example setup using company, but you could remove the company stuff, since all you really need is to add the modified completion-at-point function.
;; required packages: company bash-completion
(eval-when-compile
(require cl-lib))
;; locally redefine comint-line-beginning-position so bash-completion
;; can work in sh-mode
(defun sh-bash-completion ()
(cl-letf (((symbol-function 'comint-line-beginning-position)
#'(lambda ()
(save-excursion
(sh-beginning-of-command)
(point)))))
(let ((syntax (syntax-ppss)))
(and (not (or (nth 3 syntax)
(nth 4 syntax)))
(bash-completion-dynamic-complete)))))
;; put this in your sh-mode hook
(defun sh-completion-setup ()
;; add capf function
(add-hook 'completion-at-point-functions
'sh-bash-completion nil 'local)
(company-mode)
(make-local-variable 'company-backends)
;; use company completion-at-point
(delq 'company-capf company-backends)
(cl-pushnew 'company-capf company-backends)
(setq-local company-transformers
'(company-sort-by-backend-importance)))
(add-hook 'sh-mode-hook 'sh-completion-setup)
I have a simple elisp interactive function that I use to launch a Clojure repl.
(defun boot-repl ()
(interactive)
(shell-command "boot repl wait &"))
It opens an *Async Shell Command* buffer, and after a while the following text appears :
nREPL server started on port 59795 on host 127.0.0.1 - nrepl://127.0.0.1:59795
Implicit target dir is deprecated, please use
the target task instead. Set BOOT_EMIT_TARGET=no to disable implicit
target dir.
I would like to monitor the output of this command to be able to parse the port ("59795" in this example).
Even just the first line (in the case without warnings) would be alright.
This way I could be able to use another command to connect to the Clojure REPL waiting for me.
I cannot use shell-command-to-string as the command does not return and it blocks emacs forever (boot repl wait is supposed to last for my whole programming session, possibly more).
There may be something easy to do with cider also, but I haven't found it.
So, how do I parse the result of an asynchronous bash command in Elisp ?
Alternatively, how can I set-up Cider to launch this REPL for my and connect to it ?
To answer the question directly, you can definitely parse the output of an asyncronous shell command, using start-process and set-process-filter:
(let ((proc (start-process "find" "find" "find"
(expand-file-name "~") "-name" "*el")))
(set-process-filter proc (lambda (proc line)
(message "process output: %s" line))))
(Docs for filter function)
However, note that line above is not necessarily a line, and may include multiple lines or broken lines. Your filter is called whenever the process or emacs decides to flush some ouput:
...
/home/user/gopath/src/github.com/gongo/json-reformat/test/json-reformat-test.el
/home/user/gopath/src/github.com/gongo/json-reformat/test/test-
process output: helper.el
In your case, this could mean that your port number might be broken into two separate process-filter calls.
To fix this, we can introduce a line-buffering and line-splitting wrapper, which calls your filter for each process output line:
(defun process-filter-line-buffer (real-filter)
(let ((cum-string-sym (gensym "proc-filter-buff"))
(newline (string-to-char "\n"))
(string-indexof (lambda (string char start)
(loop for i from start below (length string)
thereis (and (eq char (aref string i))
i)))))
(set cum-string-sym "")
`(lambda (proc string)
(setf string (concat ,cum-string-sym string))
(let ((start 0) new-start)
(while (setf new-start
(funcall ,string-indexof string ,newline start))
;;does not include newline
(funcall ,real-filter proc (substring string start new-start))
(setf start (1+ new-start)));;past newline
(setf ,cum-string-sym (substring string start))))))
Then, you can safely expect your lines to be whole:
(let* ((test-output "\nREPL server started on port 59795 on host 127.0.0.1 - \nrepl://127.0.0.1:59795 Implicit target dir is deprecated, please use the target task instead. Set BOOT_EMIT_TARGET=no to disable implicit target dir.")
(proc (start-process "echo-test" "echo-test" "echo" test-output)))
(set-process-filter proc (process-filter-line-buffer
(lambda (proc line)
(when (string-match
"REPL server started on port \\([0-9]+\\)"
line)
(let ((port (match-string 1 line)))
;;take whatever action here with PORT
(message "port found: %s" port)))))))
Finally, I'm not familiar with cider but this kind of low-level work probably belongs in an inferior-type mode and has probably already been solved.
shell-command
allows to name optional output- and error-buffers. Than the error should appear inside the latter and not clutter the output any more.
A better answer to the other one I provided is to simply use cider as you suggested:
(progn
(package-refresh-contents)
(package-install 'cider)
(cider-jack-in))
Im trying to use CLISP on Windows. So, when I start it in Command line I see next
*** - SYSTEM::DRIVER: Character #\u0414 cannot be represented in the character set CHARSET:cp437
Break 1 [3]>
How can I fix this?
This is an FAQ:
What do these error messages mean: “invalid byte #x94 in CHARSET:ASCII conversion” and “character #\u00B3 cannot be represented in the character set CHARSET:ASCII”?
This means that you are trying to read (“invalid byte”) or write (“character cannot be represented”) a non-ASCII character from (or to) a character stream which has ASCII :EXTERNAL-FORMAT. The default is described in -Edomain encoding.
This may also be caused by filesystem access. If you have files with names incompatible with your CUSTOM:*PATHNAME-ENCODING*, filesystem access (e.g., DIRECTORY) will SIGNAL this ERROR. You will need to set CUSTOM:*PATHNAME-ENCODING* or pass -Edomain encoding to CLISP. Using a “1:1” encoding, such as CHARSET:ISO-8859-1, should help you avoid this error.
Note that this error may be signaled by the “Print” part of the read-eval-print loop and not by the function you call. E.g., if file "foo" contains non-ASCII characters, you will see such an error when you type
(WITH-OPEN-FILE (s "foo"
:direction :input
:EXTERNAL-FORMAT CHARSET:ISO-8859-1)
(READ-LINE s))
If instead you type
(WITH-OPEN-FILE (s "foo"
:direction :input
:EXTERNAL-FORMAT CHARSET:ISO-8859-1)
(SETQ l (READ-LINE s))
NIL)
CLISP will just print NIL and signal the error when you type l.
cp437 seems to indicate a code page. Code page 437 is "US-ASCII" if I remember correctly, that is only 7 bits. It seems that you need to configure your "Command line" to display unicode.
I have the following Common Lisp code:
(defun micro-read-eval-print ()
(format t "Micro > ")
(let ((form (read-line)))))
When I run it, I get the following:
CL-USER> (micro-read-eval-print)
(m-quote a)
Micro > NIL
Note that I typed in "(m-quote a)", while the Lisp interpreter output "Micro > NIL".
Now, I would have expected these events to happen in the reverse order. I would have expected "Micro > " to have been printed first since the format statement comes first. Why isn't it printed first? And what do I have to do to make sure it is printed first?
Try adding
(defun micro-read-eval-print ()
(format t "Micro > ")
(finish-output)
(let ((form (read-line)))))
I believe you are encountering the buffering of standard io (stdio) which, in C, is commonly bypassed via fflush() in that language.
finish-output appears to be the Common Lisp equivalent of C standard library's fflush.
I'm trying to use ack-grep as a replacement for grep + find in Emacs on Windows, but ack-grep exits immediately (successfully) without printing any matches. I've tried just about every conceivable combination of command-line parameters to ack-grep, but nothing seems to work.
M-x grep-find
Enter "ack html" to search for files containing "html". Ack exits immediately, printing nothing:
-*- mode: grep; default-directory: "c:/" -*-
Grep started at Tue Feb 23 23:50:52
ack html
Grep finished (matches found) at Tue Feb 23 23:50:52
Executing the same command "ack html" in cmd.exe works fine (showing lots of various files containing the string "html".
Any ideas?
When running ack under Emacs in Windows, I found it sometimes got confused about whether it was supposed to search files or read from STDIN. Here's the function I use to call ack (use M-x ack). You can put this in .emacs.
(defvar ack-command "ack --nogroup --nocolor ")
(defvar ack-history nil)
(defvar ack-host-defaults-alist nil)
(defun ack ()
"Like grep, but using ack-command as the default"
(interactive)
; Make sure grep has been initialized
(if (>= emacs-major-version 22)
(require 'grep)
(require 'compile))
; Close STDIN to keep ack from going into filter mode
(let ((null-device (format "< %s" null-device))
(grep-command ack-command)
(grep-history ack-history)
(grep-host-defaults-alist ack-host-defaults-alist))
(call-interactively 'grep)
(setq ack-history grep-history
ack-host-defaults-alist grep-host-defaults-alist)))
(setq grep-find-command "ack <nul -i ")
I had a similar issue using the ack-and-a-half module, closing STDIN did the trick there too. There's an issue on github: https://github.com/jhelwig/ack-and-a-half/issues/23