[cl-who-devel] escaping attributes question

Simon Cusack scusack at fastmail.com.au
Tue May 22 06:02:51 UTC 2007


Hi Mac,

>> ----- Original message -----
>> From: "Mac Chan" <emailmac at gmail.com>
>> To: "General interest list about cl-who" <cl-who-devel at common-lisp.net>
>> Date: Mon, 21 May 2007 21:50:12 -0700
>> Subject: Re: [cl-who-devel] escaping attributes question
>>  
>> On 5/21/07, Simon Cusack <scusack at fastmail.com.au> wrote:
>> > I have read the syntax and semantics chapter and was just wondering why
>> > values in the attribute position aren't escaped by default?  Or is there
>> > something I am missing?
>>  
>> I guess it's the same reason why body text ain't escaped by default,
>> because the lib can't assume too much about that data that you are
>> feeding.

Values in the attribute position get treated differently to body text
already.  They are included in the html stream by default and the
result of lisp forms are emited when used in this position.

It just feels natural to me that it goes the extra step and also
escapes the value so that it is 'safe'.

>> In my experience 95% of the time the attribute values are constant
>> html attributes like :width "100%" or :colspan 2 etc that don't
>> require escaping.
>>  
>> But your use case might be different.
>> -- Mac

Well I'm in the unfortunate position where we regularly included the
results of calculations as attributes for a javascript library to hook
into.  Something like the following is pretty common.

(defun emit-ajaxy-button (url)
  (with-html-output (*html-stream*)
    ((:input :type :button :onclick (format nil
    "javascript:doMyAjaxyThing('~A')" url)) "whatever...")))

It gets pretty messy having to remember which ones to escape all the
time.

I patched my convert-attributes (added calls to escape-string code in
all caps) 
to do the following;

(defun convert-attributes (attr-list)
  "Helper function for CONVERT-TAG-TO-STRING-LIST which converts the
alist ATTR-LIST of attributes into a list of strings and/or Lisp
forms."
  (declare (optimize speed space))
  (loop with =var= = (gensym)
        with attribute-quote = (string *attribute-quote-char*)
        for (attr . val) in attr-list
        unless (null val) ;; no attribute at all if VAL is NIL
          if (constantp val)
            if (and (eq *html-mode* :sgml) (eq val t)) ; special case
            for SGML
              nconc (list " " (string-downcase attr))
            else
              nconc (list " "
                          ;; name of attribute
                          (string-downcase attr)
                          (format nil "=~C" *attribute-quote-char*)
                          ;; value of attribute
                          (cond ((stringp val)
                                 ;; a string, just use it - this case is
                                 ;; actually not necessary because of
                                 ;; the last case
                                 (ESCAPE-STRING val))
                                ((eq val t)
                                 ;; VAL is T, use attribute's name
                                 (string-downcase attr))
                                (t
                                 ;; constant form, PRINC it -
                                 ;; EVAL is OK here because of CONSTANTP
                                 (ESCAPE-STRING (FORMAT NIL "~A" (eval
                                 val)))))
                          attribute-quote)
            end
          else
            ;; do the same things as above but at runtime
            nconc (list `(let ((,=var= ,val))
                          (cond ((null ,=var=))
                                ((eq ,=var= t)
                                 ,(case *html-mode*
                                        (:sgml
                                         `(htm ,(format nil " ~A"
                                                        (string-downcase
                                                        attr))))
                                        ;; otherwise default to :xml
                                        mode
                                        (t
                                         `(htm ,(format nil " ~A=~C~A~C"
                                                        (string-downcase
                                                        attr)
                                                        *attribute-quote-char*
                                                        (string-downcase
                                                        attr)
                                                        *attribute-quote-char*)))))
                                (t
                                 (htm ,(format nil " ~A=~C"
                                 (string-downcase attr)
                                               *attribute-quote-char*)
                                      (STR (ESCAPE-STRING (FORMAT NIL
                                      "~A" ,=var=)))
                                      ,attribute-quote)))))))

It seems like a sane thing to do to me but was wondering if I had
missed the CL-WHO way of doing the same thing.

Any comments would be most appreciated.

- sim. 



More information about the Cl-who-devel mailing list