Connecting to interactive tools using Chicken Scheme's `process`? - io

I am trying to figure out Chicken Scheme's process procedure.
When I run it in the interpreter, this example code does what I expect (prints out "Hello, world!"):
(use posix)
(let-values (((in out pid) (process "echo \"Hello, world!\"")))
(print (read-line in)))
Based on this, my expectation is that if I echo \"Hello, world!\" with bc I should see the first line of bc's usual preamble:
bc 1.06
Instead, I get a blank line, and I don't get the interpreter's prompt back again until I hit Ctrl-C.
Or, sometimes I get this error:
thread is registered for I/O on unknown file-descriptor: 33 (expected 31)
[]
...more...
<syntax>
<syntax>
<syntax>
<syntax>
<syntax>
<syntax>
<syntax>
<syntax>
<syntax>
<syntax>
<syntax>
<eval>
<eval>
<eval>
<eval>
<eval> <--
and I get kicked back out to my shell prompt.
Additionally, how do I get the process I have started to respond to input? I expect the following to print 4, but instead get a blank line again:
(let-values (((in out pid) (process "bc")))
(begin
(display "2 + 2" out)
(print (read-line in))))
(How) can I use process to have an interactive dialog with a tool like bc?

There are several components to this question. The first you already figured out: bc will detect it's not running on a terminal so it won't print the banner.
The second is simple line buffering: In UNIX, simply printing to a port will not actually send out any data, unless you explicitly flush or send a newline. In CHICKEN, this can be done either by calling flush-output on the desired port, or appending a newline to the display call. I think bc will read until it sees a newline anyway, so flushing is not likely to help here. So either call newline on the port, or use (display "2 + 2\n" out) instead of (display "2 + 2" out).
The third is an actual bug: the error you get about an unexpected file descriptor. I've filed a bug report for that, thanks for pointing it out!

Related

Why do I need to call `clear` after `initscr` with ncurses?

I have solved this problem in the sense I have code that does what I want, but I don't understand why it is necessary to do what I do, and I cannot see this behaviour documented, so I wonder if someone could explain why?
I am actually "porting" ncurses to Forth. More correctly I am writing some RISC-V assembly that lightly wraps around the C library calls to give Forth users an interface to ncurses.
This is what the code looks like in Forth:
: hello S" Hello, World!" ;
: goodbye S" Goodbye, World! " ;
: garish 1 color_red color_yellow init_pair ;
: cool 2 color_cyan color_black init_pair ;
: doit initscr clear start_color garish cool 1 color_pair attron 10 10 movew hello drop printw refresh getch 1 color_pair attroff ;
: endit 2 color_pair attron 30 30 movew goodbye drop printw refresh getch 2 color_pair attroff endwin ;
doit
endit
For those who don't know Forth this defines a few words then calls the doit and endit words to actually execute the program. But this isn't a question about Forth but about why I need to call clear (which is simply a wrapper around a call to the ncurses clear call) after I call initscr (again just a simple wrapper around a call to the ncurses function).
The assembly is shown below. 'CODEEND' and 'CODEHEADER' are macros that generate Forth function headings and 'TAILMOD' generates the return to the loop code - but the key point is you can see these are very light wrappers around the C calls:
CODEEND INITSCR, 0x01
#(--)
call initscr
la t0, STDSCR #store stdscr
sd a0, 0(t0)
TAILMOD t1
CODEHEADER CLEAR, INITSCR, 0x01
#( -- )
call clear
TAILMOD t1
If I don't call clear, then on every subsequent call of the doit endit pair I just see the previous output - i.e. initscr has not cleared the screen without this explicit call to clear.
Am I right in thinking that initscr should normally clear the screen on every invocation (at the next call of refresh)?
Is it just a function of my terminal type (this is a tty over ssh in this case) that I have to call clear or is there something else at work here?
Adding Just to be totally clear, I know the documentation says a call to refresh is required to clear the screen and I have done that but it doesn't work - that is implicit in the code I've posted where there is an explicit refresh but I am adding this to make it obvious to people who maybe don't grok Forth. (Originally where the clear is there was a refresh but it does not clear the screen on subsequent calls.)
Per manpage:
initscr also causes the first call to refresh(3x) to clear the screen.
You won't see any change on the screen until that refresh. Some functions (such as getch) do a refresh as a side-effect.

Argument list too long error in while loop reading from infinite input stream

I have a script that prints my volume status. It checks the output of pactl subscribe to determine when something has changed. Currently I'm doing this with a while loop, and after the script has been running for a certain period of time (I can replicate quickly by holding a key to toggle mute for about a minute), the only output is "/usr/bin/grep: Argument list too long"
I've tried using < <(pactl subscribe), piping into the while loop, and also reading from a fifo. None of these work. Is this expected? If so, what would be the way to handle something like pactl subscribe that prints infinite output? Since the first error mentioned ponymix, I thought it might be an issue there, but using pamixer instead fixes nothing either.
The full script is here. Here is a relevant excerpt:
while read -r event; do
if echo "$event" | grep --quiet --invert-match --ignore-case "client"; then
print_volume
fi
done < <(pactl subscribe)
I expect no errors. The first error is line 36: /usr/bin/ponymix: Argument list too long. The second error is line 36: /usr/bin/grep: Argument list too long. Then afterwards all output is line 88: /usr/bin/grep: Argument list too long.
Edit: This is not the same issue as the suggested duplicate caused by passing a long argument list to something. I am not using globbing like in that example.
The issue is that inside the print_volume function, I was repeatedly sourcing a file with exports in it. As pointed out by Charles Duffy, this caused the environment size to be too large.

Getting Common Lisp Process ID on Linux

I am wondering if there is a way to get Linux's PID (Process ID) from Common Lisp's REPL. That is, I would like to know the ID of the SBCL or Allegro process from the REPL of the process itself.
There's nothing in the Common Lisp specification that implements this. Process IDs are too implementation-dependent.
In SBCL, the SB-POSIX package provides Lisp interfaces to most POSIX system calls, so you would use (sb-posix:getpid).
In Allegro CL, operating system interface functions are in the EXCL.OSI package, so you would use (excl.ose:getpid)
There is a (basically) portable way to do this. CL provides for reading files and one can observe that the pid of the current process is in /proc/self/status (also /proc/self is a symlink to the process’ pid but I don’t think there’s a portable read link).
Specifically /proc/self/status is a text file and contains a line that looks like:
Pid: 439
So you could parse the file to extract that.
But then once you have the pid there isn’t much you can do with it without system calls or /proc weirdness
Final Solution (to the biggest part by #Dan Robertson's and #coredump - thank you guys!)
Actually #Dan Robertson gave the full answer - I realize in retrospect.
This answer is just the implementation of what he said. So give him the points!
(ql:quickload "CL-PPCRE") ;; for regex parsing
(defun get-this-pid ()
"Return PID of this current lisp process."
(with-open-file (in #P"/proc/self/status")
(loop for line = (read-line in nil)
while line
when (ppcre:scan "^Pid" line)
do (return (car
(ppcre:all-matches-as-strings "\\d+"
line))))))
;; to get current process id, call:
(get-this-pid) ;
;; returns for me at the moment using sbcl "12646"
;; this is correct as closing of all other sbcl processes
;; and doing "pidof sbcl" in the shell showed.
As #Don Robertson pointed out, the file /proc/self/status shows the program which opens it its "PID" number (every program sees it differently). Thank you Don, since this solves the problem of finding really the PID of the program (pidof sbcl in the shell would give several numbers if several lisp programs are running independently on the machine.
Calling external programs is obsolete, if we open this file then from within cl, like #coredump pointed out.
PID numbers of other programs
;; Thanks to #coredump - who suggested to use
;; `ppcre:split :whitespace-char-class` for capturing arbitrary numbers
;; in the answer string - I added a test for integer-string-p to clean
;; non-numberic values after split.
(ql:quickload "CL-PPCRE")
(defun integer-string-p (string)
"Does string constist only of '01234567890' characters?"
(reduce (lambda (x y) (and x y))
(mapcar (lambda (c) (member c (coerce "1234567890" 'list)))
(coerce string 'list))))
(defun extract-integers-from-string (s)
"Return integer-words of s."
(let ((l (ppcre:split :whitespace-char-class s)))
(remove-if-not #'integer-string-p l)))
(defun pid-numbers (program-name)
"Return PID numbers of a program in current machine."
(let ((pid-line (with-output-to-string (out)
(external-program:run "pidof" (list program-name)
:output out))))
(extract-integers-from-string pid-line)))
;; call it
(pid-numbers "sbcl")
(pid-numbers "firefox")
;; * (pid-numbers "sbcl")
;; ("16636" "12346")
;; * (pid-numbers "firefox")
;; ("24931" "19388" "19122" "10800" "10745") ; yeah I have many open :D

Simulating rolling dice on a dynamic webpage using Bash

I'm currently working on a Bash script that simulates rolling a number of 6-sided dice. This is all taking place within a virtual machine running Debian that's acting as a server. Essentially, my webpage simulates rolling the dice by using the query string to determing the number of dice to be rolled.
For instance, if my URL is http://127.0.0.1/cgi-bin/rolldice.sh?6, I want the webpage to say "You rolled 6 dice" and then, on the next line, print six numbers between 1 and 6 inclusive (that are of course "randomly" generated).
Currently, printing out the "You rolled x dice" header is working fine. However, I'm having trouble with the next part. I'm very new to Bash, so possibly the syntax or something similar is wrong with my loop. Here it is:
for i in {1..$QUERY_STRING }; do
dieRoll = $(( $RANDOM % 6 + 1))
echo $dieRoll
done
Can anyone help me figure out where I'm going wrong? I'll be happy to post the rest of rolldice.sh if needed.
Since .. requires its arguments to be literals, you have to use eval to substitute the variable:
for i in $(eval "echo {1..$QUERY_STRING}"); do
Or if you have the seq command, you can do:
for i in $(seq 1 "$QUERY_STRING")
I recommend the latter -- using eval with input from the user is very dangerous.

Running a main function inside another main function on a .hs file changes the behaviour IO?

I'm currently finishing my first Haskell Project and, on the final step of the work, my I/O function seems to behave strangely after I connected the different haskell files.
I have a main file (f1.hs) which loads some info of a multimedia library and saves it into variables on a new .hs file (f2.hs). I also have a "data processing and user interface" file (f3.hs), which reads those variables and, depending on what the user orders, it sorts them and displays them on screen. This f3.hs file works with menus, commanded by the valus of the keyboard input (getLine).
In order to make the work sequence "automatic", I made a "main" function on the f1.hs file, which creates the f2.hs file and then with the System.Cmd module, I did a system "runhaskell f3.hs". This routes the user from the f1.hs file to the main function of f3.hs.
The strange thing is that, after I did that, all the getLine seem to appear before the last line of the function prompt.
What it should appear would be:
Question One.....
Answer: (cursor's place)
but what I get is:
Question One.....
(cursor's place)
Answer:
This only happens if I runhaskell f1.hs. If I try to runhaskell f3.hs directly, it works correctly (though I can't do it on the final job, as the f2.hs file needs to be created first). Am I doing something wrong with this sequence?
I'm sorry for the lack of code, but I thought that it wouldn't be any help for the understanding of the problem...
This is typically caused by line buffering, meaning the text does not actually get printed to the console until a newline is printed. The solution is to manually flush the buffer, i.e. something like this:
import System.IO
main = do ...
putStr "Answer: "
hFlush stdout
...
Alternatively, you can disable buffering by using hSetBuffering stdout NoBuffering, though this comes at a slight performance cost, so I recommend doing the flushing manually when you need to print a partial line.

Resources