From: Mike Kozlowski
Subject: Getting a number from a string
Date: 
Message-ID: <c54dq5$8kk$1@reader1.panix.com>
So I'm trying to use Common Lisp in place of Perl for some of my
light scripting, just for the hell of it.  Only, I'm having a problem.

What I want to do is pretty simple:  I want to get a string from a
user and treat it as a number.  If it's empty or non-numeric, I want
to put in a default value.

This is trivial to do in Perl, where I can just coerce the string to a
number; it's not much harder in Java, where I can just use the
Double.parseDouble() method and catch an exception if it fails.

But I'm at a loss as to how I'd do this in Common Lisp.  I've got the
(read-line) part of it figured out just fine, but can't find a simple
way to get that string into a number.  Looking around at Web
resources, the suggestions I see are:

1) Use 'read-from-string', which won't give me the behavior I'm
looking for -- I'd want an input of "(+ 2 3)" to give me my default,
not 5.  This also makes me a bit nervous from a security standpoint
(yes, I know there's a magic variable that makes it safe), and seems
like overkill in any case.

2) Roll my own float parser.  Yeah.  No.  Not in 2004, I'm not.

So, I guess the question is:  Am I missing a more obvious way of doing
this?  Or, failing that, is there a set of standardized libraries
that provides a float parser?  I saw a link to a float parsing library
on CLiki, but it looked like it was just some guy's utility package,
and I'm not wildly crazy about picking up a bunch of random utility
libraries for basic functionality.

-- 
Mike Kozlowski
http://www.klio.org/mlk/

From: Matthew Danish
Subject: Re: Getting a number from a string
Date: 
Message-ID: <20040408211918.GB25328@mapcar.org>
On Thu, Apr 08, 2004 at 08:48:05PM +0000, Mike Kozlowski wrote:
> 1) Use 'read-from-string', which won't give me the behavior I'm
> looking for -- I'd want an input of "(+ 2 3)" to give me my default,
> not 5.  This also makes me a bit nervous from a security standpoint
> (yes, I know there's a magic variable that makes it safe), and seems
> like overkill in any case.

You are confusing READ and EVAL.  (read-from-string "(+ 2 3") => (+ 2 3).
How about:

(defun parse-float (string &optional (default 0.0))
  (let ((*read-eval* nil)
	(val (ignore-errors (read-from-string string))))
    (typecase val
      (number val)
      (t default))))

> So, I guess the question is:  Am I missing a more obvious way of doing
> this?  Or, failing that, is there a set of standardized libraries
> that provides a float parser?  I saw a link to a float parsing library
> on CLiki, but it looked like it was just some guy's utility package,
> and I'm not wildly crazy about picking up a bunch of random utility
> libraries for basic functionality.

If the float parsing library was `parse-number', it is my random utility
package.  Actually, it's fairly easy to use, you just put the file
somewhere and load it when you want; or you can put it somewhere that
ASDF knows about along with the .asd file.  However, the above function
will probably do for you, if you don't mind invoking the potentially
heavy machinery of READ.

-- 
; Matthew Danish <·······@andrew.cmu.edu>
; OpenPGP public key: C24B6010 on keyring.debian.org
; Signed or encrypted mail welcome.
; "There is no dark side of the moon really; matter of fact, it's all dark."
From: Matthew Danish
Subject: Re: Getting a number from a string
Date: 
Message-ID: <20040408213131.GC25328@mapcar.org>
On Thu, Apr 08, 2004 at 05:19:18PM -0400, Matthew Danish wrote:
> (defun parse-float (string &optional (default 0.0))
>   (let ((*read-eval* nil)
     ^ LET*, sorry
> 	(val (ignore-errors (read-from-string string))))
>     (typecase val
>       (number val)
>       (t default))))

-- 
; Matthew Danish <·······@andrew.cmu.edu>
; OpenPGP public key: C24B6010 on keyring.debian.org
; Signed or encrypted mail welcome.
; "There is no dark side of the moon really; matter of fact, it's all dark."
From: ··········@YahooGroups.Com
Subject: Re: Getting a number from a string
Date: 
Message-ID: <REM-2004apr14-004@Yahoo.Com>
> From: Matthew Danish <·······@andrew.cmu.edu>
> > (defun parse-float (string &optional (default 0.0))
> >   (let ((*read-eval* nil)  ^ LET*, sorry
> >         (val (ignore-errors (read-from-string string))))
> >     (typecase val
> >       (number val)
> >       (t default))))

I have a more general use for this sort of safety, so I've stripped off
the stuff about the number and kept just this part:

(defun read-safely-from-string (str)
  (let ((*read-eval* nil))
    (ignore-errors (read-from-string str))))

;;Test cases that all work as expected:
(read-safely-from-string "(+ 4 5) (- 4 5)") ;(+ 4 5) ;8
(read-safely-from-string "(+ 4 5") ;NIL ;#<END-OF-FILE {90229DD}>
(read-safely-from-string "#.(+ 4 5)") ;NIL ;#<READER-ERROR {9023C5D}>
(read-safely-from-string "#,(+ 4 5)") ;NIL ;#<READER-ERROR {9024865}>

;;Test case that I can live with:
(read-safely-from-string "") ;NIL ;0

;;Problem case, prints on standard-output as side-effect of trying to read:
(read-safely-from-string ")")
Warning:  Ignoring unmatched close parenthesis at file position 1.

NIL
1

I'm using CMUCL. Is there any way I can disable printing anything to
standard-output from inside read-from-string? (Of course I can do the
grossly ugly thing of diverting standard-output and error-output to
/dev/null or to a temporary file, but is there a better alternative?)
(with-open-file (ochan "/dev/null" :direction :output)
  (let ((*error-output* ochan) (*gc-verbose* nil) ...) ...))
Maybe divert output to a string-stream instead, and return the resultant
string as a third return value?
From: Simon Katz
Subject: Re: Getting a number from a string
Date: 
Message-ID: <c5kbs4$2p3jf$1@ID-131024.news.uni-berlin.de>
<··········@YahooGroups.Com> wrote in message
······················@Yahoo.Com...

> I'm using CMUCL. Is there any way I can disable printing anything to
> standard-output from inside read-from-string? (Of course I can do
> the grossly ugly thing of diverting standard-output and error-output
> to /dev/null or to a temporary file, but is there a better
> alternative?)
> (with-open-file (ochan "/dev/null" :direction :output)
>   (let ((*error-output* ochan) (*gc-verbose* nil) ...) ...))
> Maybe divert output to a string-stream instead, and return the
> resultant string as a third return value?

(let ((*standard-output* (make-broadcast-stream)))
  ...)
From: Christophe Rhodes
Subject: Re: Getting a number from a string
Date: 
Message-ID: <sqisg243ui.fsf@lambda.dyndns.org>
··········@YahooGroups.Com writes:

> (defun read-safely-from-string (str)
>   (let ((*read-eval* nil))
>     (ignore-errors (read-from-string str))))
>
> ;;Test case that I can live with:
> (read-safely-from-string "") ;NIL ;0
>
> ;;Problem case, prints on standard-output as side-effect of trying to read:
> (read-safely-from-string ")")
> Warning:  Ignoring unmatched close parenthesis at file position 1.
>
> NIL
> 1
>
> I'm using CMUCL. Is there any way I can disable printing anything to
> standard-output from inside read-from-string? 

Anything?  Just diagnostic warnings?  Depending on your use case,
these may be different things.

> (Of course I can do the grossly ugly thing of diverting
> standard-output and error-output to /dev/null or to a temporary
> file, but is there a better alternative?)

(make-broadcast-stream) is the Lisp analogue to /dev/null for output.
That may be preferable; alternatively, you might want to consider
something like

(defun read-safely-from-string (string)
  (let ((*read-eval* nil))
    (handler-bind
        ((error #'abort)
         ;; people may differ about the stylishness or otherwise of
         ;; this handler, but I think it's safe.
         (condition (lambda (c) (signal c) 
                      (let ((muffle (find-restart 'muffle-warning c)))
                        (when muffle
                          (invoke-restart muffle))))))
      (read-from-string string))))

This should quieten the system as much as possible, while still
potentially letting you recover from out of memory conditions or the
like.

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: ··········@YahooGroups.Com
Subject: Re: Getting a number from a string
Date: 
Message-ID: <REM-2004apr19-001@Yahoo.Com>
Here's what happens if I just use read-from-string:

(loop
  (format t "~%Input: ") (finish-output)
  (format t "(sexpr indexInString) = ~S~%"
    (multiple-value-list (read-from-string (read-line)))))

Input: 42
(sexpr indexInString) = (42 2)
;;Correct

Input: (+ 3 5)
(sexpr indexInString) = ((+ 3 5) 7)
;;Correct

Input: #.(+ 3 5)
(sexpr indexInString) = (8 9)
;;*** DANGER WILL ROBINSON ***


Here was my earlier attempt to fix the danger:

(defun read-safely-from-string (str)
  (let ((*read-eval* nil))
    (ignore-errors (read-from-string str))))

(loop
  (format t "~%Input: ") (finish-output)
  (format t "(sexpr indexInString) = ~S~%"
    (multiple-value-list (read-safely-from-string (read-line)))))

Input: (+ 3 4)
(sexpr indexInString) = ((+ 3 4) 7)
;;Correct as before

Input: #.(+ 3 4)
(sexpr indexInString) = (NIL #<READER-ERROR {91BF32D}>)
;Danger all gone!

Input: )
Warning:  Ignoring unmatched close parenthesis at file position 1.
(sexpr indexInString) = (NIL 1)
;Nuisance I asked about best way to fix.


Trying the code suggested by Christophe Rhodes:

(defun read-safely-from-string (string)
  (let ((*read-eval* nil))
    (handler-bind
        ((error #'abort)
         (condition (lambda (c) (signal c)
                      (let ((muffle (find-restart 'muffle-warning c)))
                        (when muffle
                          (invoke-restart muffle))))))
      (read-from-string string))))

(loop
  (format t "~%Input: ") (finish-output)
  (format t "(sexpr indexInString) = ~S~%"
    (multiple-value-list (read-safely-from-string (read-line)))))

Input: (+ 3 5)
(sexpr indexInString) = ((+ 3 5) 7)
;;Correct as always

Input: #.(+ 3 5)

*
;;No good! It aborts completely out of my program loop!!


Trying the suggestion of several people including Simon Katz:

(defun read-safely-from-string (str)
  (let ((*read-eval* nil)
        (*standard-output* (make-broadcast-stream)))
    (ignore-errors (read-from-string str))))

(loop
  (format t "~%Input: ") (finish-output)
  (format t "(sexpr indexInString) = ~S~%"
    (multiple-value-list (read-safely-from-string (read-line)))))

Input: 42
(sexpr indexInString) = (42 2)
;;Correct as always

Input: #.(+ 3 4)
(sexpr indexInString) = (NIL #<READER-ERROR {9200455}>)
;;Correct as I obtained before

Input: )
Warning:  Ignoring unmatched close parenthesis at file position 1.
(sexpr indexInString) = (NIL 1)
;;Nope, not fixed, maybe it's error output instead of standard output?


(defun read-safely-from-string (str)
  (let ((*read-eval* nil)
        (*error-output* (make-broadcast-stream)))
    (ignore-errors (read-from-string str))))

(loop
  (format t "~%Input: ") (finish-output)
  (format t "(sexpr indexInString) = ~S~%"
    (multiple-value-list (read-safely-from-string (read-line)))))

Input: )
(sexpr indexInString) = (NIL 1)
;;Fixed again

Input: #.(+ 3 5)
(sexpr indexInString) = (NIL #<READER-ERROR {9244F75}>)
;;But this is correct too, danger gone

Input: #,(+ 3 5)
(sexpr indexInString) = (NIL #<READER-ERROR {92458DD}>)
;;Likewise this is fixed

Input: nil
(sexpr indexInString) = (NIL 3)
;;Correct

Input:   )
(sexpr indexInString) = (NIL 3)
;;Indistinguishable from nil actually typed in by user.
;;I think I can live with this ambiguity.
;;At least it's not a security leak.


Aha, here's how to fix it all:

(defun read-safely-from-string (str)
  (let ((*read-eval* nil)
        (*error-output* (make-broadcast-stream)))
    (ignore-errors (read-from-string str :preserve-whitespace t))))

(loop
  (format t "~%Input: ") (finish-output)
  (format t "(sexpr indexInString) = ~S~%"
    (multiple-value-list (read-safely-from-string (read-line)))))

Input: )
(sexpr indexInString) = (NIL #<END-OF-FILE {900FA05}>)
;;Fixed

Input:   )
(sexpr indexInString) = (NIL #<END-OF-FILE {9010AF5}>)
;;Fixed, no longer confused with input of nil

Input: nil
(sexpr indexInString) = (NIL 3)
;;Still working as always

Input: #.(list 3 4)
(sexpr indexInString) = (NIL #<READER-ERROR {9011B6D}>)
;;Still fixed, phew!

Input: 42
(sexpr indexInString) = (42 2)
;;Still working as always

Input: (list 3 4)
(sexpr indexInString) = ((LIST 3 4) 10)
;;Still working as always

So I guess that's what I'll use to parse s-expressions
from decoded HTML forms in a CGI/CMUCL application,
unless there's some security problem I haven't realized.
Does anybody have experience parsing s-expressions from Web
clients (from HTML forms), using read-from-string and/or variants?
From: John Thingstad
Subject: Re: Getting a number from a string
Date: 
Message-ID: <opr6o9hio2xfnb1n@news.chello.no>
Depends how paranoid you are.
Setting *read-eval* to nil eleminates the possibillty of running arbitrary 
code
but your version is still suseptibe to DOS attacks.
Consider "(((((((((((((((((((((((((((((((((((((((...." until you run out 
of memory.
To be absoluteley sure you must limit the length of the string you read to 
something
reasonable.
Altso remember that read-error returns two values. If return is nil the 
second value
is the error message. A normal approach is to log this.
(multiple-value-setq and with-output-stream are your friends)

> So I guess that's what I'll use to parse s-expressions
> from decoded HTML forms in a CGI/CMUCL application,
> unless there's some security problem I haven't realized.
> Does anybody have experience parsing s-expressions from Web
> clients (from HTML forms), using read-from-string and/or variants?

-- 
Sender med M2, Operas revolusjonerende e-postprogram: http://www.opera.com/
From: ··········@YahooGroups.Com
Subject: Re: Getting a number from a string
Date: 
Message-ID: <REM-2004apr26-001@Yahoo.Com>
> Date: Mon, 19 Apr 2004 11:45:08 +0100
(Sorry for delay in responding. I have a big queue of unanswered
newsgroup articles I've recently discovered. Tonight I've moved yours
up to the head of the list because this topic is urgent.)

> From: John Thingstad <··············@chello.no>
> Setting *read-eval* to nil eleminates the possibillty of running
> arbitrary code but your version is still suseptibe to DOS attacks.

I don't understand how that is possible. Please describe an environment
whereby somebody elsewhere on the net connects to my LISP server-level
appliation service and somehow denies other users of my service the
ability to use my service. Please convince me there's a real problem
I'm missing. See below for my arguent why it's impossible, and please
try to find a flaw in my arguent.

> Consider "(((((((((((((((((((((((((((((((((((((((...." until you run
> out of memory.

I don't run out of memory. The process running the service for that
specific user runs out of memory and crashes, which doesn't affect any
other users of my service. Or if I'm using mod_lisp (don't know if I
ever will because I don't know if it'll ever exist and be available on
my ISP) it might crash the shared server process, but I believe I can
protect against that problem easily, see below.

> To be absoluteley sure you must limit the length of the string you
> read to something reasonable.

If it's even a problem, that solution is trivial to implement.

> Also remember that read-error returns two values. If return is nil
> the second value is the error message. A normal approach is to log
> this. (multiple-value-setq and with-output-stream are your friends)

But I have to be careful to limit the size of the log, else a malicious
user could set up trojaned MS-Windows systems to flood my service with
illegal requests that make the log so huge that my total disk
allocation is exceeded, and then other users won't be able to use any
service that writes even one byte of temporary data to the disk.
Still, it's simple to limit the size of any particular file.
Still, such a problem should be in the security checklist.

Now the promised argument why there's no security problem (for me) in
the first place: All my proposed LISP WebServer applications use CGI.
The way it works is that:
(1) The user's client-program (browser) connects to my ISP's Web
server, issues a URL specifying one of my CGI applications. With the
GET method, the encoded HTML FORM is passed in the URL after the
question ark. With the POST method, the same data is passed by some
other mechanism on the side.
(2) My ISP's Web server reads all of the GET or POST data from the
user's client-program and stores it in a system buffer.
(3) My ISP's Web server passes a bunch of system/global variables to my
CGI application, one of which is the number of characters of client
data that have been buffered. In the case of the GET method, all that
data (up to 255 characters) is in a system variable. In the case of the
POST method, all that data is queued on standard input. (Note: If my
service immediately crashes to the read-eval-print debug loop, all that
encoded data will be passed to that debug loop, so I have to be careful
my program does (quit) at the first sign of trouble instead of entering
a debug loop already at this point.)
(4) My LISP/CGI application reads a system global to determine whether
the method is GET or POST, and checks the other system global for the
length to see whether it's too large to be reasonable, and then either
issues a brief failure message and does (quit), or reads into a LISP
string the entire contents of the encoded HTML FORM. At this point, all
user data is in that string, not in standard input, so the worst that
can happen from this point on if an unprotected error happens is that
the program goes into a break loop whereupon it tries to read from
standard input and sees EOF which causes an error which throws it into
a second level of break loop whereupon it tries to read from standard
input and sees EOF which causes ... infinite loop of spew back to the
user, but no way the user can pass any malicious data back to the
server during this particular HTTP connection.
(5) My LISP/CGI application performs decoding of the encoded HTML FORM
to yield an association list, each element of the form
(formFieldName . correspondingValue).
(6) My LISP/CGI application inspects specific eleents in that
association list to determine the identification of the user and the
current state of that user's process and the specific input data
supplied by the user, and proceeds with whatever the application needs
to do next for that user.

So if I make sure the size of the encoded HTML FORM is less than a few
megabytes, doing (quit) immediately if the length is too large, and my
application doesn't crash before exhausting standard input with the
POST method, and my application disables read-time evaluation when
reading s-expressions from any of the field values from the association
list, I don't see how a malicious user can compromise security via my
LISP/CGI application. What did I overlook?
From: Frank A. Adrian
Subject: Re: Getting a number from a string
Date: 
Message-ID: <pan.2004.04.26.14.49.36.648690@ancar.org>
On Mon, 26 Apr 2004 06:16:34 -0800, RobertMaas wrote:

> What did I overlook?

The fact that the originating machine (or network thereof) can throw
enough requests at your server within a given period to turn it to mush
(that's the main way in which DoS is done today).  This takes work on the
part of your ISP to deal with load appropriately, check for too
many duplicate source addresses within a given unit time, and other ISP
magic.  However, in the final analysis, you're still responsible for
handling the flood of requests (possibly bogus) that come your way.  How
good is your system at separating good from bogus requests?  If you have
one good request in a sea of 100000 bogus ones within a second, can your
system stil process and respond to the good request in a timely manner? 
Is your CGI system configured to detect potential DoS activity below the
threshold of your ISP's detection ability, but still high enough to kill
your server?  If this goes on for hours, does your machine overheat and
fail?

My main point is that there are many levels of security and reliability. 
Just because your system doesn't crash under what you consider a single
DoS attack vector doean't mean that your system is secure against these
types of attack in general. This has little to do with Lisp specifically
- it's an issue that needs to be handled systemically regardless of
application or server implementation language (but, as usual, it is
easier to do this in Lisp). But dealing with this kind of attack more
effectively generally requires increased interaction between the
application and the server.

Not to kibbitz too much, but this means that you really should be using
CL-HTTP as your web server so that you can transfer data between the two
more easily to harden the combination of the app and the server
against DoS attacks - and that's what you were talking about originally.

faa
From: ··········@YahooGroups.Com
Subject: Re: Getting a number from a string
Date: 
Message-ID: <REM-2004may02-009@Yahoo.Com>
> From: "Frank A. Adrian" <·······@ancar.org>
> If you have one good request in a sea of 100000 bogus ones within a
> second, can your system stil process and respond to the good request
> in a timely manner?

I assume the admin of my ISP would notice the activity and realize they
were not legitimate use of my CGI application, so the admin would block
connections from whereever they were coming from, perhaps entire blocks
of dialup space on an ISP filled with trojan-compromised MicroSoft
Windows systems.

> This has little to do with Lisp specifically

Correct. Also it has nothing specifically to do with my account on this
ISP, or my CGI application, or anything else I have control over.

> you really should be using CL-HTTP as your web server

I don't have that option. I can't mandate that the ISP, where I'm just
one of hundreds of users, switch to a different server just to make it
easier for me.

> to harden the combination of the app and the server against DoS
> attacks - and that's what you were talking about originally.

No, I wasn't. I was concerned that somebody might use read-time eval to
sneak in a trojan that could run any program whatsoever from my
account, and thereby delete my files or do even worse under my account.
Then secondarily I was concerned that spurious printouts such as
unmatched-close-parens warnings from inside read-from-string would mess
up the nice output format my program was trying to send to the Web
user.

The two applications which I haven't yet implemented because of this
trojan concern are:

Something that teaches LISP over the Web, by letting student type in an
s-expression and my program parses it (using read-from-string) and then
judges it per what the student was supposed to be trying at that time.

An s-expression-based remote-procedure-call facility via HTTP, whereby
s-expressions are passed back and forth for calling and return-values
respectively, using read-from-string and (format nil ...) respectively.
From: Christopher C. Stacy
Subject: Re: Getting a number from a string
Date: 
Message-ID: <un04p8j3f.fsf@news.dtpq.com>
>>>>> On Sun, 02 May 2004 23:50:42 -0800, RobertMaas  ("RobertMaas") writes:
 RobertMaas> The two applications which I haven't yet implemented
 RobertMaas> because of this trojan concern are:

 RobertMaas> Something that teaches LISP over the Web, by letting
 RobertMaas> student type in an s-expression and my program parses it
 RobertMaas> (using read-from-string) and then judges it per what the
 RobertMaas> student was supposed to be trying at that time.

I recommend writing your own Lisp interpreter (written in Lisp, of course)
for just that purpose.  This interpreter could also return instructional
messages when the student tries to do something wrong, rather than just
sending back the Lisp error message.  By the way, wouldn't you want to
keep the state of the student's Lisp session available somehow, so that
he can enter more than one Lisp expression?   If you're using CGI, you'll
need to store and re-create that externally somehow (including EQ issues,
which are doubtless going to be something the student is concerned about).
From: ··········@YahooGroups.Com
Subject: Re: Getting a number from a string
Date: 
Message-ID: <REM-2004may12-002@Yahoo.Com>
> From: ······@news.dtpq.com (Christopher C. Stacy)
> I recommend writing your own Lisp interpreter (written in Lisp, of
> course) for just that purpose.  This interpreter could also return
> instructional messages when the student tries to do something wrong,
> rather than just sending back the Lisp error message.

Yes, I agree. Thanks for suggesting it. I had planned to merely
traverse the student's input s-expression making sure only "authorized"
functions are mentionned in the function position, and use a safe-read
and safe-eval to trap syntax and runtime errors respectively, and
simply report the LISP system's error back to the student. But I've
come to realize that since I must traverse the input s-expression to
find what functions are called, I might as well go whole-hog and
emulate EVAL myself as I do the traversal. But given that I have no
funds to pay for my time, and nobody interested in my work anyway, I
might go back to the lazy route anyway if I ever feel like spending any
of my unpaid time at all on this project.

My original idea is to have somebody come to my apartment, and I dial
into my ISP and run CMUCL on my shell account. But it's difficult to
arrange that with strangers. If I'm at a place that has TELNET, I can
TELNET to my shell account and then it's pretty much the same as from
home. (The North-County self-help center has TELNET available, and so
does the computer lab at De Anza college.) But if TELNET is not
available, such as at public libraries where a Web browser is the only
program available (and some libraries such as the new King library at
San Jose State also have MicroSoft Word and Excel available, but still
no TELNET), I'd like to have something via CGI that I can use to run
both my Read-TellTypeOfObject-Print first-lesson loop and something
like a regular Read-Eval-Print loop for all lessons from second onward.
I want it safe enough that if somebody guesses my password and gets
into my CGI program without me around to supervise, they won't be able
to do anything bad on my account on my ISP. With me sitting next to the
student, the CGI program emulating EVAL and automatically  giving nice
explanations isn't necessary because I can verbally explain anything
not obvious.

> By the way, wouldn't you want to keep the state of the student's Lisp
> session available somehow, so that he can enter more than one Lisp
> expression?   If you're using CGI, you'll need to store and re-create
> that externally somehow (including EQ issues, which are doubtless going
> to be something the student is concerned about).

I didn't mention this because it wasn't relevant to the original
question of how to do a safe-read when the person submitting
s-expressions to be parsed is untrusted, but from the start I
envisioned keeping a session-so-far that builds up one more step for
each CGI transaction. But to avoid all problems of EQ, I re-build
everything from input strings each time another transaction occurs.
One problem: I'll have to be careful about data that may change out
from under the student, for example:

In: (get-universal-time)
Out: 3293378358

In: (format nil "~S" *)
Out: "3293378358"

In: (position #\5 *)
Out: 8

In: (subseq ** *)
Out: "58"

On subsequent transactions, the UT will be different, so the position
of the #\5 may be different, or even NIL, and if NIL then the subseq
will bomb out. Maybe I'll keep the output string as well as the input
string, whenever the output string differs on re-generation from what
it was before I'll warn the student at that point, so later when
something bombs out when it worked before it won't be a huge surprise.
In case of error, I'll probably want the program to truncate the I/O
sequence at the point just before the input that caused the error. If
there's something later, not dependent on the stuff that worked then
started failing, copy&paste from the previous screen (using the BACK
button on browser, copy&paste to NotePad), then FORWARD button and
copy&paste just the desired stuff from NotePad, should be good enough
with me sitting there advising.
From: Christopher C. Stacy
Subject: TELNET programs [Re: Getting a number from a string]
Date: 
Message-ID: <uoeot1jft.fsf_-_@news.dtpq.com>
A wonderful thing is that from a web browser, you can download
and execute the "PuTTY" program (goggle for it) and have a very
nice telnet/ssh available whereever you go.  It's also small
enough to fit on a floppy.  (If they have the system restricted
so that you can't run an EXE file, you're screwed, of course.)

Also, in some browsers you can say a URL like:  TELNET://mit-mc.arpa//
From: Christophe Rhodes
Subject: Re: Getting a number from a string
Date: 
Message-ID: <sqllksmed7.fsf@lambda.dyndns.org>
··········@YahooGroups.Com writes:

> Trying the code suggested by Christophe Rhodes:
>
> (defun read-safely-from-string (string)
>   (let ((*read-eval* nil))
>     (handler-bind
>         ((error #'abort)
>          (condition (lambda (c) (signal c)
>                       (let ((muffle (find-restart 'muffle-warning c)))
>                         (when muffle
>                           (invoke-restart muffle))))))
>       (read-from-string string))))
>
> (loop
>   (format t "~%Input: ") (finish-output)
>   (format t "(sexpr indexInString) = ~S~%"
>     (multiple-value-list (read-safely-from-string (read-line)))))
>
> Input: (+ 3 5)
> (sexpr indexInString) = ((+ 3 5) 7)
> ;;Correct as always
>
> Input: #.(+ 3 5)
>
> *
> ;;No good! It aborts completely out of my program loop!!

You mean your program loop doesn't have an abort restart?

(loop
  (with-simple-restart (abort "Return to program toplevel")
    (format t "~%Input: ") (finish-output)
    (format t "(sexpr index-in-string) = ~S~%"
      (multiple-value-list (read-safely-from-string (read-line))))))

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: ··········@YahooGroups.Com
Subject: Re: Getting a number from a string
Date: 
Message-ID: <REM-2004may05-005@Yahoo.Com>
> From: Christophe Rhodes <·····@cam.ac.uk>
> You mean your program loop doesn't have an abort restart?

Correct. At the time I started using CL circa 1990, using Steele's 1984
book as my reference, that wasn't yet in the standard, and this is the
first time anybody mentionned that it would be a good thing to learn
now. Is there a quick checklist somewhere of the major new features,
such as intercepting ERROR signals via restarts, that have been added
to CL since the original spec, so I know which parts of the online
manual I really ought to take a hard look at someday to see what I've
been missing ever since the ANSI standard came into being?

> (loop
>   (with-simple-restart (abort "Return to program toplevel")
>     (format t "~%Input: ") (finish-output)
>     (format t "(sexpr index-in-string) = ~S~%"
>       (multiple-value-list (read-safely-from-string (read-line))))))

Well that's one way to design this algorithm. A different design choice
would be to put the simple restart inside the read-safely-from-string
so that control never passes outside of that function except via normal
return. I'd need to think which is better for my purposes. Or maybe
neither is a good idea in this context, namely interactively debugging
something intended for eventual use in a CGI-based serverside
application whereby there is no within-the-process interaction
possible, only whole transactions where HTML FORM contents are
submitted to server and a new HTML FORM is returned to the client
(browser).
From: Kenny Tilton
Subject: Re: Getting a number from a string
Date: 
Message-ID: <Pijdc.7098$mX.3350555@twister.nyc.rr.com>
Mike Kozlowski wrote:
> So I'm trying to use Common Lisp in place of Perl for some of my
> light scripting, just for the hell of it.  Only, I'm having a problem.
> 
> What I want to do is pretty simple:  I want to get a string from a
> user and treat it as a number.  If it's empty or non-numeric, I want
> to put in a default value.
> 
> This is trivial to do in Perl, where I can just coerce the string to a
> number; it's not much harder in Java, where I can just use the
> Double.parseDouble() method and catch an exception if it fails.
> 
> But I'm at a loss as to how I'd do this in Common Lisp.  I've got the
> (read-line) part of it figured out just fine, but can't find a simple
> way to get that string into a number.  Looking around at Web
> resources, the suggestions I see are:
> 
> 1) Use 'read-from-string', which won't give me the behavior I'm
> looking for -- I'd want an input of "(+ 2 3)" to give me my default,
> not 5.

What Lisp are you using which returned 5 when you tried?:

     (read-from-string "(+ 2 3)")

kt

-- 
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: Mike Kozlowski
Subject: Re: Getting a number from a string
Date: 
Message-ID: <c54it8$ao4$1@reader1.panix.com>
In article <·····················@twister.nyc.rr.com>,
Kenny Tilton  <·······@nyc.rr.com> wrote:

>> 1) Use 'read-from-string', which won't give me the behavior I'm
>> looking for -- I'd want an input of "(+ 2 3)" to give me my default,
>> not 5.
>
>What Lisp are you using which returned 5 when you tried?:
>
>     (read-from-string "(+ 2 3)")

Apparently, I'm just a moron.  I'd swear I tried it and got that
result, but no, clisp properly treats it as an error.  I have no idea
why I was thinking that read-from-string was doing an eval.

Okay, I think I've got this now.  This seems to work, giving me a
number if I type it, and a default of 30 otherwise:

(print (if (numberp (setq input (read-from-string (read-line)))) input 30))

(I suppose that's the cue for a dozen people to point out that it only
looks like it works, and I'm doing something very stupid...)

-- 
Mike Kozlowski
http://www.klio.org/mlk/
From: Mike Kozlowski
Subject: Re: Getting a number from a string
Date: 
Message-ID: <c54k68$bb8$1@reader1.panix.com>
In article <············@reader1.panix.com>,
Mike Kozlowski  <···@klio.org> wrote:

>Okay, I think I've got this now.  This seems to work, giving me a
>number if I type it, and a default of 30 otherwise:
>
>(print (if (numberp (setq input (read-from-string (read-line)))) input 30))

... And I see that, one message down, Christopher Stacy has given
similar code that has the virtue of actually working properly.  I'll
shut up and go back to reading now.

Thanks for the help.

-- 
Mike Kozlowski
http://www.klio.org/mlk/
From: Kenny Tilton
Subject: Re: Getting a number from a string
Date: 
Message-ID: <Rtldc.7183$mX.3409877@twister.nyc.rr.com>
Mike Kozlowski wrote:
> In article <·····················@twister.nyc.rr.com>,
> Kenny Tilton  <·······@nyc.rr.com> wrote:
> 
> 
>>>1) Use 'read-from-string', which won't give me the behavior I'm
>>>looking for -- I'd want an input of "(+ 2 3)" to give me my default,
>>>not 5.
>>
>>What Lisp are you using which returned 5 when you tried?:
>>
>>    (read-from-string "(+ 2 3)")
> 
> 
> Apparently, I'm just a moron.

That was close. I'm assigned to "troll patrol" this month and was ready 
to put the dogs on you as a Perl troll, since no lisp would have done 
that. Glad I held my fire, but the dogs still need a run. :(

>  I'd swear I tried it ...

Yes, that just happened to me on a loop question. the answer looked a 
helluva lot like what I had tried.

> and got that
> result, but no, clisp properly treats it as an error.

I think you mean "...when you tried to use '(+ 2 3) as a number".

>  I have no idea
> why I was thinking that read-from-string was doing an eval.
> 
> Okay, I think I've got this now.  This seems to work, giving me a
> number if I type it, and a default of 30 otherwise:
> 
> (print (if (numberp (setq input (read-from-string (read-line)))) input 30))
> 
> (I suppose that's the cue for a dozen people to point out that it only
> looks like it works, and I'm doing something very stupid...)

Well, that's the fun part around here. Every newby Q or code snippet 
gets batted around until it belongs in a PhD oral exam.


kt

-- 
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: André Thieme
Subject: Re: Getting a number from a string
Date: 
Message-ID: <c54ujh$umf$2@ulric.tng.de>
Kenny Tilton wrote:

>> Mike Kozlowski wrote:
>>
>> Apparently, I'm just a moron.
> 
> 
> That was close. I'm assigned to "troll patrol" this month and was ready 
> to put the dogs on you as a Perl troll, since no lisp would have done 
> that. Glad I held my fire, but the dogs still need a run. :(

Perhaps you let them catch a musician.
Preferably one who plays the cello ;)


Andr�
--
From: David Steuber
Subject: Re: Getting a number from a string
Date: 
Message-ID: <873c7ajqr4.fsf@david-steuber.com>
Kenny Tilton <·······@nyc.rr.com> writes:

> Well, that's the fun part around here. Every newby Q or code snippet
> gets batted around until it belongs in a PhD oral exam.

I never feel like my time is wasted here.  I'm still in the very
basics stage by my reckoning.  So to me, it was useful to not only
read this thread, but to also try this in my REPL:

CL-USER> (read-from-string "(+ 2 3)")
(+ 2 3)
7
CL-USER> 

While I still have to look up read-from-string in the CLHS, it looks
like a Lisp list was returned, or just the string reprsentation of the
same, and the number of characters read as if the return was generated
by (values &rest).

I've very recently decided to write a Lisp application server that
runs behind either mod_lisp on Apache (most likely) or mod_fastcgi
(perhaps), or even have the Lisp image do its own HTTP and use Apache
as a forward proxy (probably not).  So this thread is very relevent to
me as I will want to take data from mod_lisp or string data from
standard input and be able to make sure it is safe to do things with
it (as in sanitize untrusted data to make it trustworthy).

-- 
It would not be too unfair to any language to refer to Java as a
stripped down Lisp or Smalltalk with a C syntax.
--- Ken Anderson
    http://openmap.bbn.com/~kanderso/performance/java/index.html
From: Coby Beck
Subject: Re: Getting a number from a string
Date: 
Message-ID: <c5co6t$29h7$1@otis.netspace.net.au>
"David Steuber" <·····@david-steuber.com> wrote in message
···················@david-steuber.com...
> CL-USER> (read-from-string "(+ 2 3)")
> (+ 2 3)
> 7
> CL-USER>
>
> While I still have to look up read-from-string in the CLHS, it looks
> like a Lisp list was returned, or just the string reprsentation of the
> same,

CL-USER 19 > (read-from-string "(+ 2 3)")
(+ 2 3)
7

CL-USER 20 > (type-of *)
CONS

CL-USER 21 > (stringp **)
NIL

You get the object created by read.

-- 
Coby Beck
(remove #\Space "coby 101 @ big pond . com")
From: Christopher C. Stacy
Subject: Re: Getting a number from a string
Date: 
Message-ID: <u4qruyxrl.fsf@news.dtpq.com>
>>>>> On Thu, 8 Apr 2004 20:48:05 +0000 (UTC), Mike Kozlowski ("Mike") writes:

 Mike> Use 'read-from-string', which won't give me the behavior I'm
 Mike> looking for -- I'd want an input of "(+ 2 3)" to give me my
 Mike> default, not 5.  This also makes me a bit nervous from a
 Mike> security standpoint (yes, I know there's a magic variable that
 Mike> makes it safe), and seems like overkill in any case.

I'm not sure what you're afraid of.

(defun read-a-number (&key stream default)
  (let* ((line (read-line stream))
         (n (let* ((*read-eval* nil))
              (ignore-errors (read-from-string line nil)))))
    (if (numberp n)
        n
      default)))

You could perform additional editing or checking on the raw 
input line, if you wanted.  You could split out all but the 
NUMBERP part into a seperate READ-SAFELY function if you wanted.
From: Alexander Schreiber
Subject: Re: Getting a number from a string
Date: 
Message-ID: <slrnc7bh54.nuu.als@mordor.angband.thangorodrim.de>
Mike Kozlowski <···@klio.org> wrote:
> So I'm trying to use Common Lisp in place of Perl for some of my
> light scripting, just for the hell of it.  Only, I'm having a problem.
> 
> What I want to do is pretty simple:  I want to get a string from a
> user and treat it as a number.  If it's empty or non-numeric, I want
> to put in a default value.

So use read-from-string.
 
> This is trivial to do in Perl, where I can just coerce the string to a
> number; it's not much harder in Java, where I can just use the
> Double.parseDouble() method and catch an exception if it fails.
> 
> But I'm at a loss as to how I'd do this in Common Lisp.  I've got the
> (read-line) part of it figured out just fine, but can't find a simple
> way to get that string into a number.  Looking around at Web
> resources, the suggestions I see are:
> 
> 1) Use 'read-from-string', which won't give me the behavior I'm
> looking for -- I'd want an input of "(+ 2 3)" to give me my default,
> not 5. 

it doesn't, it returns an S-Expression:
[1]> (read-from-string "(+ 2 3)")
(+ 2 3) ;
7
[1]> (type-of (read-from-string "(+ 2 3)"))
CONS

As well as it should - only when you actually eval the first return
value of read-from-string will you get the result of (+ 2 3).

If you get 5 from reading "(+ 2 3)", then there seems to be an eval
lurking around.

> This also makes me a bit nervous from a security standpoint
> (yes, I know there's a magic variable that makes it safe), and seems
> like overkill in any case.
> 
> 2) Roll my own float parser.  Yeah.  No.  Not in 2004, I'm not.

read-from-string does that just fine:

[2]> (read-from-string "34e-2")
0.34 ;
5

> So, I guess the question is:  Am I missing a more obvious way of doing
> this?  Or, failing that, is there a set of standardized libraries
> that provides a float parser?

Parsing floats comes for free as an ability of the Common Lisp reader,
so just use it.

Regards,
      Alex.
-- 
"Opportunity is missed by most people because it is dressed in overalls and
 looks like work."                                      -- Thomas A. Edison
From: Edi Weitz
Subject: Re: Getting a number from a string
Date: 
Message-ID: <m3fzbeqhhv.fsf@bird.agharta.de>
On Thu, 8 Apr 2004 20:48:05 +0000 (UTC), Mike Kozlowski <···@klio.org> wrote:

> So I'm trying to use Common Lisp in place of Perl for some of my
> light scripting, just for the hell of it.  Only, I'm having a
> problem.
>
> What I want to do is pretty simple: I want to get a string from a
> user and treat it as a number.  If it's empty or non-numeric, I want
> to put in a default value.

  <http://www.cliki.net/parse-number>

Edi.
From: André Thieme
Subject: Re: Getting a number from a string
Date: 
Message-ID: <c54ugc$umf$1@ulric.tng.de>
Mike Kozlowski wrote:
> So I'm trying to use Common Lisp in place of Perl for some of my
> light scripting, just for the hell of it.  Only, I'm having a problem.
> 
> What I want to do is pretty simple:  I want to get a string from a
> user and treat it as a number.  If it's empty or non-numeric, I want
> to put in a default value.
> 
> This is trivial to do in Perl, where I can just coerce the string to a
> number; it's not much harder in Java, where I can just use the
> Double.parseDouble() method and catch an exception if it fails.
> 
> But I'm at a loss as to how I'd do this in Common Lisp.  I've got the
> (read-line) part of it figured out just fine, but can't find a simple
> way to get that string into a number.  Looking around at Web
> resources, the suggestions I see are:
> 
> 1) Use 'read-from-string', which won't give me the behavior I'm
> looking for -- I'd want an input of "(+ 2 3)" to give me my default,
> not 5.  This also makes me a bit nervous from a security standpoint
> (yes, I know there's a magic variable that makes it safe), and seems
> like overkill in any case.
> 
> 2) Roll my own float parser.  Yeah.  No.  Not in 2004, I'm not.
> 
> So, I guess the question is:  Am I missing a more obvious way of doing
> this?  Or, failing that, is there a set of standardized libraries
> that provides a float parser?  I saw a link to a float parsing library
> on CLiki, but it looked like it was just some guy's utility package,
> and I'm not wildly crazy about picking up a bunch of random utility
> libraries for basic functionality.

When I was reading your post I thought that this is an ability that CL
already has to offer. What you enter into the REPL are strings, nothing
else. When I type the number "one" it is in fact (ascii) a 49. But CL
knows that when it finds a 49 that it has to treat it as a "1".
Another thought was, that you have always the whole language availabe.
So if there is anything that the REPL can do, you also can do it by
using exactly the same mechanism the REPL uses.

I don't know if my thoughts were correct, but some experts would jump in
and tell if they were not ok.


Andr�
--