;;;============================================================================ ;;; File: "url.scm" ;;; Copyright (c) 2011 by Marc Feeley, All Rights Reserved. ;;;============================================================================ (##namespace ("url#")) (##include "~~lib/gambit#.scm") (##include "url#.scm") (declare (standard-bindings) (extended-bindings) (block) (fixnum) (not safe) ) ;;;============================================================================ ;; Encoding and decoding of x-www-form-urlencoded data. (define (url-encode str #!optional (start 0) (end (string-length str)) (space-to-plus? #f)) (define chunk-length 1024) (define (hex n) (string-ref "0123456789ABCDEF" (bitwise-and n 15))) (define (encode-chunk len i end) (if (< i end) (let ((c (string-ref str i))) (if (or (and (char>=? c #\a) (char<=? c #\z)) (and (char>=? c #\A) (char<=? c #\Z)) (and (char>=? c #\0) (char<=? c #\9)) (char=? c #\-) (char=? c #\_) (char=? c #\.) (char=? c #\!) (char=? c #\~) (char=? c #\*) (char=? c #\') (char=? c #\() (char=? c #\)) (and (char=? c #\space) space-to-plus?)) (let ((result (encode-chunk (+ len 1) (+ i 1) end))) (string-set! result len (if (and (char=? c #\space) space-to-plus?) #\+ c)) result) (let ((result (encode-chunk (+ len 3) (+ i 1) end))) (let ((n (char->integer c))) (string-set! result (+ len 0) #\%) (string-set! result (+ len 1) (hex (arithmetic-shift n -4))) (string-set! result (+ len 2) (hex n)) result)))) (make-string len))) (let loop ((i start) (chunks '())) (let ((span (min (- end i) chunk-length))) (if (= span 0) (##append-strings ;; apply string-append (reverse chunks)) (let* ((next-i (+ i span)) (chunk (encode-chunk 0 i next-i))) (loop next-i (cons chunk chunks))))))) (define (url-decode str #!optional (start 0) (end (string-length str)) (plus-to-space? #f)) (define chunk-length 1024) (define (hex? c) (cond ((and (char>=? c #\0) (char<=? c #\9)) (- (char->integer c) (char->integer #\0))) ((and (char>=? c #\a) (char<=? c #\f)) (+ 10 (- (char->integer c) (char->integer #\a)))) ((and (char>=? c #\A) (char<=? c #\F)) (+ 10 (- (char->integer c) (char->integer #\A)))) (else #f))) (define (decode len i end) (if (and (< i end) (< len chunk-length)) (let ((c (string-ref str i))) (cond ((or (and (char>=? c #\a) (char<=? c #\z)) (and (char>=? c #\A) (char<=? c #\Z)) (and (char>=? c #\0) (char<=? c #\9)) (char=? c #\-) (char=? c #\_) (char=? c #\.) (char=? c #\!) (char=? c #\~) (char=? c #\*) (char=? c #\') (char=? c #\() (char=? c #\)) (and (char=? c #\+) plus-to-space?)) (let ((result (decode (+ len 1) (+ i 1) end))) (and result (begin (string-set! (cdr result) len (if (and (char=? c #\+) plus-to-space?) #\space c)) result)))) ((char=? c #\%) (if (< (+ i 2) end) (let* ((a (hex? (string-ref str (+ i 1)))) (b (hex? (string-ref str (+ i 2))))) (and a b (let ((result (decode (+ len 1) (+ i 3) end))) (and result (begin (string-set! (cdr result) len (integer->char (+ (arithmetic-shift a 4) b))) result))))) #f)) (else #f))) (cons i (make-string len)))) (let loop ((i start) (chunks '())) (if (< i end) (let ((x (decode 0 i end))) (and x (loop (car x) (cons (cdr x) chunks)))) (##append-strings ;; apply string-append (reverse chunks))))) ;;;============================================================================