#lang scheme
(require
 scheme/port)

(define-values (current-terminal-input-port current-terminal-output-port)
  (let-values ([(in out) (open-input-output-file "/dev/tty" #:exists 'update)])
    (values (make-parameter in) (make-parameter out))))

(define (read-stty-result in)
  (cond
    [(regexp-try-match #px"^([^\\s=;]+)\\s*=\\s*([^\\s;]+)[\\s;]*" in)
     => (match-lambda
          [(list _ key value)
           (map bytes->string/utf-8 (list key value))])]
    [(regexp-try-match #px"^([^\\s;]+)(?:\\s+baud)?[\\s;]*" in)
     => (match-lambda
          [(list _ command)
           (bytes->string/utf-8 command)])]
    [else
     (read-line in)]))

(define (stty [commands '("--save")] [terminal (current-terminal-input-port)])
  (let-values ([(child in out err) (apply subprocess #f terminal #f "/bin/stty" commands)])
    (let ([results (flatten (port->list read-stty-result in))]
          [message (port->string err)])
      (subprocess-wait child)
      (if (zero? (subprocess-status child))
          (if (null? results)
              (void)
              results)
          (error 'stty "could not perform ~e: ~a" commands message)))))

(provide/contract
 [current-terminal-input-port (parameter/c (and/c input-port? terminal-port?))]
 [current-terminal-output-port (parameter/c (and/c output-port? terminal-port?))]
 [stty (->* () ((listof string?) (and/c input-port? terminal-port?)) (or/c (listof string?) void?))])
