Subject: Re: Business Opportunities for SF Bay Lispers?
From: (Rob Warnock)
Date: Sat, 20 Mar 2004 10:57:39 -0600
Newsgroups: comp.lang.lisp,comp.lang.scheme
Message-ID: <>
Lupo LeBoucher <> wrote:
| Um, yeah; in an ideal world, someone would take CPAN and shoe-horn it into 
| CMU-CL (or even just shoe-horn the basic functionality of Perl itself 
| into Lisp, like Graham is trying to do with Arc), and that could make a 
| Lisp which is far more handy for "glue" code than Perl is, but nobody has 
| done that, so we're stuck with shitty scripting languages for day to day 
| work, or reimplementing language A in language B.

Well, just because CL isn't *perfect* for scripting doesn't mean it's
not awfully darned useful!! I use CMUCL for scripting lots of stuff.
Some recent examples:

"date-nist" -- fetches the current time from (one or more of)
    the NIST time server(s). The heart of it is this function:

	(defun fetch-time/rfc868 (host)
	  (let* ((fd (connect-to-inet-socket host 37))
		 (stream (system:make-fd-stream
			  :element-type '(unsigned-byte 8)
			  :buffering :none)))
	    (with-open-stream (s stream)
	      (loop repeat 4			; [Thanks, Erik!]
		    for time = (read-byte s)
			     then (+ (read-byte s) (* time 256))
		finally (return time)))))

"csv_to_html" -- Convert a CSV (Comma-Separated Variables) file into
    an HTML table. The heart of it is this function:

	;;; PARSE-CSV-LINE -- Parse one CSV line into a list of fields,
	;;; stripping quotes and field-internal escape characters.
	(defun parse-csv-line (line)
	  (when (or (string= line "")           ; special-case blank lines
		    (char= #\# (char line 0)))  ; or those starting with "#"
	    (return-from parse-csv-line '()))
	  (loop for c across line
		with state = 'normal
		and results = '()
		and chars = '() do
	    (ecase state
	       (case c
		 ((#\") (setq state 'quoted))
		 ((#\\) (setq state 'escaped))
		  (push (coerce (nreverse chars) 'string) results)
		  (setq chars '()))
		 (t (push c chars))))
	       (case c
		 ((#\") (setq state 'normal))
		 ((#\\) (setq state 'quoted+escaped))
		 (t (push c chars))))
	      ((escaped) (push c chars) (setq state 'normal))
	      ((quoted+escaped) (push c chars) (setq state 'quoted)))
	       ;; close still-open field
	       (push (coerce (nreverse chars) 'string) results)
	       (return (nreverse results)))))

"keto" -- Given a number of grams of protein, carbohydrate, and fat,
    show the total number of calories and the "ketogenic ratio" [useful
    to those on low-carb diets]:

	% cat ~/bin/keto
	#!/usr/local/bin/cmucl -script
	  ((= 3 (length *script-args*))
	   (destructuring-bind (p c f)
	       (mapcar #'read-from-string *script-args*)
	     (format t "grams: protein ~a  carb ~a  fat ~a~%" p c f)
	     (format t "ketogenic ratio: ~a~%"
		    (/ (+ (* 0.9 f) (* 0.46 p))
		       (+ (* 1.0 c) (* 0.1 f) (* 0.58 p))))
	     (format t "total calories: ~a~%"
		       (+ (* 4 p) (* 4 c) (* 9 f)))))
	    (format t "usage: ~a <protein> <carb> <fat>~%" *script-name*)
	    (format t "(enter all amounts in grams)~%")
	    (quit 1)))
	% 8 6 17
	grams: protein 8  carb 6  fat 17
	ketogenic ratio: 1.5380875
	total calories: 209

"sum-time-user-sys" -- Do some trivial processing on the output of the
    "time" builtin command in "csh". [Shown in a previous post today.]

"random" -- Generate random strings which are legal as URLs, filenames,
    and passwords on most systems, thus shell and URL metacharacters
    must be excluded. Takes the time of day plus a number of bytes from
    "/dev/urandom" and uses it to seed the CMUCL MT19937 random number
    generator, then crank that some number of times and encode the output
    into acceptable text [6 bits per character] of the desired length
    [default 16]:

	% random
	% random 64
	% repeat 3 random
	% repeat 5 random 8

"wild" -- Construct more complex wild-card command than are natively
    convenient in Unix shell, e.g., the sort of thing TOPS-10 used to
    let you do, stuff like "ren *.foo *.bar" [which does *not* do the
    same thing at all in Unix!!]. It's just a loop around CL's DIRECTORY
    and TRANSLATE-PATHNAME and a FORMAT to print it all. It doesn't do
    the commands itself, it just outputs them, but if you like what you
    see then, you can repeat it and pipe the output to "/bin/sh -x" to
    execute them (the "-x" lets you see what's going on). Example:

	% ls -l foo1*
	-rwxr-xr-x  1 rpw3  rpw3  2099 Feb  6 03:52 foo1
	-rw-r--r--  1 rpw3  rpw3   223 Feb  6 03:48 foo1.lisp
	-rw-r--r--  1 rpw3  rpw3   882 Feb  6 03:52 foo1.x86f
	% wild
	usage: wild command pattern [ repl-patterns... ]
	% wild mv foo1\* bar2\*
	mv foo1 bar2
	mv foo1.lisp bar2.lisp
	mv foo1.x86f bar2.x86f
	% !! | sh -x
	wild mv foo1\* bar2\* | sh -x
	+ mv foo1 bar2
	+ mv foo1.lisp bar2.lisp
	+ mv foo1.x86f bar2.x86f
	% ls -l bar2*
	-rwxr-xr-x  1 rpw3  rpw3  2099 Feb  6 03:52 bar2
	-rw-r--r--  1 rpw3  rpw3   223 Feb  6 03:48 bar2.lisp
	-rw-r--r--  1 rpw3  rpw3   882 Feb  6 03:52 bar2.x86f

Lest I bore people [if I haven't already!], I'll stop there. But suffice
it to say that the more I use CL and become familiar with various nuances,
the more I use it *instead* of scripts in "sh" or Perl (as well as in
conjunction with them).


Rob Warnock			<>
627 26th Avenue			<URL:>
San Mateo, CA 94403		(650)572-2607