From 3ffa667e8920f83bef812fb7efa246c96c3b1389 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Wed, 10 Aug 2011 11:26:11 -0400 Subject: [PATCH] Length-limited line reading --- safe-io.rkt | 26 ++++++++++++++++++++++++++ test-safe-io.rkt | 23 +++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 safe-io.rkt create mode 100644 test-safe-io.rkt diff --git a/safe-io.rkt b/safe-io.rkt new file mode 100644 index 0000000..e84f34f --- /dev/null +++ b/safe-io.rkt @@ -0,0 +1,26 @@ +#lang racket/base + +(provide read-line-limited) + +;; Uses Internet (CRLF) convention. +(define (read-line-limited port limit) + (let collect-chars ((acc '()) + (remaining limit)) + (let ((ch (read-char port))) + (cond + ((eof-object? ch) (if (null? acc) + ch + (list->string (reverse acc)))) + ((eqv? ch #\return) (let ((ch (read-char port))) + (if (eqv? ch #\linefeed) + (list->string (reverse acc)) + (error 'read-line-limited + "Invalid character ~v after #\\return" + ch)))) + ((eqv? ch #\newline) + ;; Is this a good idea? + (error 'read-line-limited "Bare #\\linefeed encountered")) + ((positive? remaining) (collect-chars (cons ch acc) (- remaining 1))) + (else (error 'read-line-limited + "Line too long (more than ~v bytes before CRLF)" + limit)))))) diff --git a/test-safe-io.rkt b/test-safe-io.rkt new file mode 100644 index 0000000..ab19243 --- /dev/null +++ b/test-safe-io.rkt @@ -0,0 +1,23 @@ +#lang racket/base + +(require "safe-io.rkt") +(require rackunit) + +(define (s str) + (open-input-string str)) + +(check-equal? (read-line-limited (s "") 5) eof) +(check-equal? (read-line-limited (s "abc") 5) "abc") +(check-equal? (read-line-limited (s "abc\r\ndef") 5) "abc") +(check-equal? (read-line-limited (s "abcxy\r\ndef") 5) "abcxy") + +(check-exn #rx"read-line-limited: Invalid character # after #\\\\return" + (lambda () (read-line-limited (s "abc\r") 5))) +(check-exn #rx"read-line-limited: Invalid character #\\\\d after #\\\\return" + (lambda () (read-line-limited (s "abc\rdef") 5))) + +(check-exn #rx"Bare #\\\\linefeed encountered" + (lambda () (read-line-limited (s "abc\ndef") 5))) + +(check-exn #rx"Line too long \\(more than 5 bytes before CRLF\\)" + (lambda () (read-line-limited (s "abcxyz\r\ndef") 5)))