#! /usr/bin/env /usr/local/bin/racket #lang racket (define (call-valid? call) (and (<= (string-length call) 9) (andmap (or/c char-numeric? char-alphabetic? (curry char=? #\-)) (string->list call)))) ;; unfortunately, we cannot just exec red and let it take over the ;; I/O. ed, being the standard text editor, only works with standard ;; line endings, \n. telnet and BPQ, however, use \r\n. \r\n upsets ;; ed, the standard text editor. so we need to wrap the input and ;; output ports ourselves in order to provide this translation. (define (ed) (match-define (list stdout stdin pid stderr proc) (process* "/usr/bin/red" "-p*" #:set-pwd? #t)) (define buffer (make-bytes 128)) (define (loop) (let ([evt-result (sync (choice-evt (read-bytes-avail!-evt buffer stdout) (read-line-evt (current-input-port) 'any)))]) (cond ;; read-line-evt results in a string. we have something to write ;; on stdin. [(string? evt-result) (displayln evt-result stdin) (flush-output stdin) (loop)] ;; read-bytes-avail!-evt results in an integer. we have ;; something to read from stdout. [(integer? evt-result) (display (bytes->string/utf-8 (subbytes buffer 0 evt-result))) (flush-output) (loop)] ;; either event may EOF, which means that ed has died. [(eof-object? evt-result) ;; clean up (close-output-port stdin) (close-input-port stdout) (close-input-port stderr) (proc 'kill)]))) (loop))