2001-04-17 Rob Browning <rlb@cs.utexas.edu>

* src/scm/utilities.scm (string-split): used to be string-split-on
	in qif-utils.scm

	* src/scm/qif-import/qif-utils.scm: move string-split-on to
	utilities.scm as string-split to better match string-join.

	* src/scm/qif-import/qif-to-gnc.scm (qif-import:qif-to-gnc):
	string-split-on -> string-split.

	* src/scm/qif-import/qif-dialog-utils.scm
	(qif-import:get-all-accts): string-split-on -> string-split.

	* src/scm/price-quotes.scm (yahoo-get-historical-quotes): new
	function - retrieve lists of historical quote information.

	* src/scm/bootstrap.scm.in (%load-path): add new guile-modules
	directory so we can use-modules from there.

	* src/guile/Makefile.am (CLEANFILES): add gnucash.c so it goes
	away on "make clean" in addition to "make distclean".

	* src/engine/gnc-pricedb.c: minor doc updates.

	* src/engine/gnc-pricedb.h: much more documentation.

	* lib/guile-www: new directory - contains guile-www CVS module.
	Used by new historical quote function.  Several new files added.
	Installed to new install directory GNC_SHAREDIR/guile-modules such
	that it is available via (use-modules (www main)), etc.

	* lib/Makefile.am (SUBDIRS): add guile-www

	* configure.in (AC_OUTPUT): add lib/guile-www/Makefile


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3988 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Dave Peticolas
2001-04-17 21:37:38 +00:00
parent b5e8d20903
commit 1a63b7f75d
21 changed files with 1296 additions and 76 deletions
+4
View File
@@ -0,0 +1,4 @@
Makefile
config.cache
config.log
config.status
+106
View File
@@ -0,0 +1,106 @@
2001-02-04 Marius Vollmer <mvo@zagadka.ping.de>
* http.scm: Use (ice-9 rdelim) if `read-line' not defined. Thanks
to Thien-Thi Nguyen.
2000-06-04 Mikael Djurfeldt <mdj@mdj.nada.kth.se>
* main.scm (www:get): Pass url as single arg.
(Thanks to Dale P. Smith.)
1998-07-27 Jim Blandy <jimb@red-bean.com>
* Test of EGCS repository access.
1997-12-24 Tim Pierce <twp@skepsis.com>
* cgi.scm (init-environment): Make server-software and
server-protocol optional.
1997-12-22 Tim Pierce <twp@skepsis.com>
* cgi.scm (read-raw-form-data): Add `cgi-' to content-length.
(parse-form): Change `url-decode' to `url:decode'.
Mon Oct 20 18:06:10 1997 Jim Blandy <jimb@totoro.red-bean.com>
* configure.in: Update version to 1.0b.
configure: Regenerated.
* Makefile.in, aclocal.m4, configure: Regenerated w/automake 1.2c.
* url.scm (url:scheme, url:host, url:port, url:path): Move
accessors up.
(url:address): New accessor for "mailto:" URLs.
(url:unknown): New accessor for unrecognized URL types.
(url:user): New accessor for "ftp" URLs.
(url:make, url:make-http, url:make-ftp, url:make-mailto): New
constructors for URL objects.
(url-regexp): Replaced by...
(http-regexp, ftp-regexp, mailto-regexp): Separate regular
expressions for different URL schemes.
(url:parse): Use the above to handle ftp and mailto URLs, and also
do something graceful with unrecognized garbage.
(url:unparse): New function, for turning a parsed url into a
string.
* http.scm (add-open-connection!, get-open-connection): Incomplete
code replaced with dummy definitions, so we can give it to Mikael
immediately.
(http:request): Close the socket after we're done reading the
body.
* http.scm (http:request): Reformatted for readability.
* http.scm (http:request): Don't expect a body in reply to a
"HEAD" request, even though we do get a "content-length" header.
Tue Jun 17 17:27:04 1997 Tim Pierce <twpierce@bio-5.bsd.uchicago.edu>
* http.scm: (http:request): Force "/" onto the beginning of the
"path" variable unconditionally. We must do this because of a
stupid incompatibility between the URL RFC and HTTP RFC.
* http.scm: (parse-status-line): New function.
(http:request): Use parse-status-line instead of
separate-fields-discarding-char (which does not work when a status
line contains more than two spaces).
(separate-fields-discarding-char): Removed.
Tue Jun 17 01:45:59 1997 Tim Pierce <twp@twp.tezcat.com>
* http.scm (http:request): Change calling conventions to accept a
method and a URL rather than a port and request string. Do some
sanity checking on arguments.
(http:get): Pass a single URL argument to http:request.
(http:open): Permit port argument to be #f in addition to '().
(display-with-crlf): Make port argument optional.
(add-open-connection!): Braino fix (set! for setq).
* Makefile: New file. (Oops.)
Mon Jun 16 17:49:20 1997 Tim Pierce <twpierce@bio-5.bsd.uchicago.edu>
Release preparations.
* Makefile.am, configure.in, install-sh, mkinstalldirs, missing:
New files.
* Makefile.in, configure, aclocal.m4: Generated, not re.
* README: New file.
Sun Jun 15 00:50:22 1997 Tim Pierce <twp@twp.tezcat.com>
* http.scm: add some support for persistent connections.
(http:connect): check get-open-connection before creating a new
socket, and call add-open-connection! after creating one.
(add-open-connection!): New function.
(get-open-connection): New function.
Mon Jun 9 23:42:32 1997 Tim Pierce <twp@twp.tezcat.com>
New WWW module.
* cgi.scm: New file.
* http.scm: New file.
* main.scm: New file.
* url.scm: New file.
* wwwcat: New file.
+5
View File
@@ -0,0 +1,5 @@
gncscmdir = ${GNC_SHAREDIR}/guile-modules/www
gncscm_DATA = cgi.scm http.scm main.scm url.scm
EXTRA_DIST = README README.gnucash wwwcat
+66
View File
@@ -0,0 +1,66 @@
This is an alpha release of the Guile WWW library, version 1.0.
Roadmap:
The (www http) library includes some support for navigating HTTP
connections. http:open, http:request and http:get may be used for
opening connections and making HTTP requests; http:make-message,
http:message-body and http:message-header may be used to
manipulate HTTP messages. Support is planned for the full
HTTP/1.1 protocol, including cookies and persistent connections.
(www url) provides url:parse for parsing a URL into its component
parts, and the selector functions url:scheme, url:host, url:port
and url:path for selecting individual components of a parsed URL.
For individual components that may have been URL-encoded in
transit, url:decode translates a string into its raw (unencoded)
form.
(www cgi) provides some functions helpful in writing CGI scripts
painlessly. The focus is on scripts to process interactive forms.
cgi:init reads any form data and initializes a CGI environment.
cgi:form-data? determines whether any form data has been returned
by a browser for processing. cgi-value returns the value
associated with a form variable, and cgi-names and cgi-values
return all of the names and values present in the current form.
(www main) provides www:get, which decodes a URL and invokes the
appropriate protocol handler for retrieving the desired object.
It is intended to be a generic interface useful for retriving data
named by any URL.
wwwcat is an example script of how www:get and other functions
might be used by a Guile application.
A generic guide to hacking on Guile software follows.
Tim Pierce
twp@tezcat.com
Hacking It Yourself ==================================================
As distributed, the Guile WWW library needs only a Unix system to build
and install. However, its makefiles, configuration scripts, and a few
other files are automatically generated, not written by hand. If you
want to make changes to the system (which we encourage!) you will find
it helpful to have the tools we use to develop it. They are the
following:
Autoconf 2.12 --- a system for automatically generating `configure'
scripts from templates which list the non-portable features a
program would like to use. Available in
"ftp://prep.ai.mit.edu/pub/gnu".
Automake 1.1p --- a system for automatically generating Makefiles that
conform to the (rather Byzantine) GNU coding standards. The
nice thing is that it takes care of hairy targets like 'make
dist' and 'make distclean', and automatically generates
Makefile dependencies. Available in
"ftp://ftp.cygnus.com/pub/tromey".
libtool 0.9d --- a system for managing the zillion hairy options needed
on various systems to produce shared libraries. Available in
"ftp://alpha.gnu.ai.mit.edu/gnu".
You are lost in a little maze of automatically generated files, all
different.
+7
View File
@@ -0,0 +1,7 @@
This is a stripped down version of the guile-www CVS module. All of
the automakery has been removed and it's been reconfigured to work
with gnucash. In the long run, we'll probably switch to use whatever
becomes the guile standard for www access.
+185
View File
@@ -0,0 +1,185 @@
;;;; cgi.scm: Common Gateway Interface support for WWW scripts.
(define-module (www cgi)
:use-module (www url))
;;;; Copyright (C) 1997 Free Software Foundation, Inc.
;;;;
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2, or (at your option)
;;;; any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this software; see the file COPYING. If not, write to
;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
;;;; Boston, MA 02111-1307 USA
;;;;
(define form-variables '())
;;; CGI environment variables.
;;; Should these all be public?
(define-public cgi-server-software-type #f)
(define-public cgi-server-software-version #f)
(define-public cgi-server-hostname #f)
(define-public cgi-gateway-interface #f)
(define-public cgi-server-protocol-name #f)
(define-public cgi-server-protocol-version #f)
(define-public cgi-server-port #f)
(define-public cgi-request-method #f)
(define-public cgi-path-info #f)
(define-public cgi-path-translated #f)
(define-public cgi-script-name #f)
(define-public cgi-query-string #f)
(define-public cgi-remote-host #f)
(define-public cgi-remote-addr #f)
(define-public cgi-authentication-type #f)
(define-public cgi-remote-user #f)
(define-public cgi-remote-ident #f)
(define-public cgi-content-type #f)
(define-public cgi-content-length #f)
(define-public cgi-http-accept-types #f)
(define-public cgi-http-user-agent #f)
;;; CGI high-level interface
;;;
;;; A typical CGI program will first call (cgi:init) to initialize
;;; the environment and read in any data returned from a form. Form
;;; data can be extracted conveniently with these functions:
;;;
;;; (cgi:values NAME)
;;; Fetch any values associated with NAME found in the form data.
;;; Returned value is a list, even if it contains only one element.
;;; (cgi:value NAME)
;;; Fetch only the CAR from (cgi:values NAME). Convenient for when
;;; you are certain that NAME is associated with only one value.
(define-public (cgi:init)
(init-environment)
(and cgi-content-length
(parse-form (read-raw-form-data)))
(and cgi-query-string
(parse-form cgi-query-string)))
(define-public (cgi:values name)
(assoc-ref form-variables name))
(define-public (cgi:value name)
;; syntactic sugar for obtaining just one value from a particular key
(let ((values (cgi:values name)))
(and values (car values))))
(define-public (cgi:names) (map car form-variables))
(define-public (cgi:form-data?) (not (null? form-variables)))
;;;; Internal functions.
;;;;
;;;; (parse-form DATA): parse DATA as raw form response data, adding
;;;; values as necessary to `form-variables'.
;;;; (read-raw-form-data): read in `content-length' bytes from stdin
;;;; (init-environment): initialize CGI environment from Unix env vars.
(define (parse-form raw-data)
;; get-name and get-value are used to parse individual `name=value' pairs.
;; Values are URL-encoded, so url:decode must be called on each one.
(define (get-name pair)
(let ((p (string-index pair #\=)))
(and p (make-shared-substring pair 0 p))))
(define (get-value pair)
(let ((p (string-index pair #\=)))
(and p (url:decode (make-shared-substring pair (+ p 1))))))
(for-each (lambda (pair)
(let* ((name (get-name pair))
(value (get-value pair))
(old-value (cgi:values name)))
(set! form-variables
(assoc-set! form-variables
name
(cons value (or old-value '()))))))
(separate-fields-discarding-char #\& raw-data)))
(define (read-raw-form-data)
(and cgi-content-length (read-n-chars cgi-content-length)))
(define (init-environment)
;; SERVER_SOFTWARE format: name/version
(let ((server-software (getenv "SERVER_SOFTWARE")))
(if server-software
(let ((slash (string-index server-software #\/)))
(set! cgi-server-software-type (substring server-software 0 slash))
(set! cgi-server-software-version (substring server-software (1+ slash))))))
(set! cgi-server-hostname (getenv "SERVER_NAME"))
(set! cgi-gateway-interface (getenv "GATEWAY_INTERFACE"));"CGI/revision"
(let* ((server-protocol (getenv "SERVER_PROTOCOL")))
(if server-protocol
(let ((slash (string-index server-protocol #\/)))
(set! cgi-server-protocol-name (substring server-protocol 0 slash))
(set! cgi-server-protocol-version (substring server-protocol (1+ slash))))))
(let ((port (getenv "SERVER_PORT")))
(set! cgi-server-port (and port (string->number port))))
(set! cgi-request-method (getenv "REQUEST_METHOD"))
(set! cgi-path-info (getenv "PATH_INFO"))
(set! cgi-path-translated (getenv "PATH_TRANSLATED"))
(set! cgi-script-name (getenv "SCRIPT_NAME"))
(set! cgi-remote-host (getenv "REMOTE_HOST"))
(set! cgi-remote-addr (getenv "REMOTE_ADDR"))
(set! cgi-authentication-type (getenv "AUTH_TYPE"))
(set! cgi-remote-user (getenv "REMOTE_USER"))
(set! cgi-remote-ident (getenv "REMOTE_IDENT"))
(set! cgi-content-type (getenv "CONTENT_TYPE"))
(set! cgi-query-string (getenv "QUERY_STRING"))
(and cgi-query-string
(string-null? cgi-query-string)
(set! cgi-query-string #f))
(let ((contlen (getenv "CONTENT_LENGTH")))
(set! cgi-content-length (and contlen (string->number contlen))))
;; HTTP_ACCEPT is a list of MIME types separated by commas.
(let ((types (getenv "HTTP_ACCEPT")))
(set! cgi-http-accept-types
(and types (separate-fields-discarding-char #\, types))))
;; HTTP_USER_AGENT format: software/version library/version.
(set! cgi-http-user-agent (getenv "HTTP_USER_AGENT")))
;;; System I/O and low-level stuff.
(define (read-n-chars num . port-arg)
(let ((p (if (null? port-arg)
(current-input-port)
(car port-arg)))
(s (make-string num)))
(do ((i 0 (+ i 1))
(ch (read-char p) (read-char p)))
((or (>= i num) (eof-object? ch)) s)
(string-set! s i ch))))
;; This is defined in #/ice-9/string-fun, but the interface is
;; weird, the semantics perverse, and it doesn't work. We use
;; a working copy here.
(define (separate-fields-discarding-char ch str)
(let loop ((fields '())
(str str))
(let ((pos (string-rindex str ch)))
(if pos
(loop (cons (make-shared-substring str (+ 1 pos)) fields)
(make-shared-substring str 0 pos))
(cons str fields)))))
+302
View File
@@ -0,0 +1,302 @@
;;;; http.scm: HTTP client library for Guile.
;;;;
(define-module (www http)
:use-module (www url)
:use-module (ice-9 regex))
;;;; Copyright (C) 1997 Free Software Foundation, Inc.
;;;;
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2, or (at your option)
;;;; any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this software; see the file COPYING. If not, write to
;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
;;;; Boston, MA 02111-1307 USA
;;;;
;;; Compatibility
(or (defined? 'read-line)
(use-modules (ice-9 rdelim)))
;;; Variables that affect HTTP usage.
(define-public http:version "HTTP/1.0") ; bump up to 1.1 when ready
(define-public http:user-agent "GuileHTTP 0.1")
;;; An HTTP message is represented by a vector:
;;; #(VERSION STATUS-CODE STATUS-TEXT HEADERS BODY)
;;;
;;; Each of VERSION, STATUS-CODE, STATUS-TEXT are strings. HEADERS
;;; is an alist of headers and their contents. BODY is a single string.
(define (http:make-message version statcode stattext headers body)
(vector version statcode stattext headers body))
;;;; HTTP status predicates.
;;;
;;; (http:message-version MSG)
;;; Returns the HTTP version in use in HTTP message MSG.
;;;
;;; (http:message-status-code MSG)
;;; Returns the status code returned in HTTP message MSG.
;;;
;;; (http:message-status-text MSG)
;;; Returns the text of the status line from HTTP message MSG.
;;;
;;; (http:message-status-ok? STATUS)
;;; Returns #t if status code STATUS indicates a successful request,
;;; #f otherwise.
(define-public (http:message-version msg) (vector-ref msg 0))
(define-public (http:message-status-code msg) (vector-ref msg 1))
(define-public (http:message-status-text msg) (vector-ref msg 2))
(define-public (http:message-status-ok? msg)
(http:status-ok? (http:status-code msg)))
(define-public (http:status-ok? status)
(char=? #\2 (string-ref status 0)))
(define-public (http:message-body msg) (vector-ref msg 4))
;;; HTTP response headers functions
;;;
;;; An HTTP message header is represented here by a pair. The CAR is a
;;; symbol representing the header name, and the CDR is a string
;;; containing the header text. E.g.:
;;;
;;; '((date . "Thu, 29 May 1997 23:48:27 GMT")
;;; (server . "NCSA/1.5.1")
;;; (last-modified . "Tue, 06 May 1997 18:32:03 GMT")
;;; (content-type . "text/html")
;;; (content-length . "8097"))
;;;
;;; Note: these symbols are all lowercase, although the original headers
;;; were mixed-case. Clients using this library should keep this in
;;; mind, since Guile symbols are case-sensitive.
;;;
;;; FIXME: should headers with known semantics be parsed automatically?
;;; I.e. should the Content-Length header automatically get string->number?
;;; Should Date and Last-Modified headers be run through strptime?
;;; It is advantageous to keep headers in a uniform format, but it may
;;; be convenient to parse headers that have unambiguous meanings.
;;;
;;; (http:message-headers MSG)
;;; Returns a list of the headers from HTTP message MSG.
;;; (http:message-header HEADER MSG)
;;; Return the header field named HEADER from HTTP message MSG, or
;;; #f if no such header is present in the message.
(define-public (http:message-headers msg) (vector-ref msg 3))
(define-public (http:message-header header msg)
(http:fetch-header header (http:message-headers msg)))
(define (http:fetch-header header header-alist)
(assq-ref header-alist header))
(define header-regex (make-regexp ": *"))
(define (http:header-parse hd)
(let ((match (regexp-exec header-regex hd)))
(cons (string->symbol
(apply string
(map char-downcase
(string->list (match:prefix match)))))
(match:suffix match))))
(define (parse-status-line statline)
(let* ((first (string-index statline #\space))
(second (string-index statline #\space (1+ first))))
(list (make-shared-substring statline 0 first)
(make-shared-substring statline (1+ first) second)
(make-shared-substring statline (1+ second)))))
;;; HTTP connection management functions.
;;;
;;; Open connections are cached on hostname in the connection-table.
;;; If an HTTP connection is already open to a particular host and TCP port,
;;; looking up the hostname and port number in connection-table will yield
;;; a Scheme port that may be used to communicate with that server.
(define connection-table '())
;; FIXME: you can only re-use a connection if the server sends the
;; Keep-Alive header, I think. With these definitions, we were trying to
;; send more requests on connections the server assumed were dead.
;; (define (add-open-connection! host tcp-port port)
;; (set! connection-table
;; (assoc-set! connection-table (cons host tcp-port) port)))
;; (define (get-open-connection host tcp-port)
;; (assoc-ref connection-table (cons host tcp-port)))
(define (add-open-connection! host tcp-port port)
#f)
(define (get-open-connection host tcp-port)
#f)
;;; HTTP methods.
;;;
;;; Common methods: GET, POST etc.
(define-public (http:get url)
;; FIXME: if http:open returns an old connection that has been
;; closed remotely, this will fail.
(http:request "GET" url))
;;; Connection-oriented functions:
;;;
;;; (http:open HOST [PORT])
;;; Return an HTTP connection to HOST on TCP port PORT (default 80).
;;; If an open connection already exists, use it; otherwise, create
;;; a new socket.
(define-public (http:open host . args)
(let ((port (cond ((null? args) 80)
((not (car args)) 80)
(else (car args)))))
(or (get-open-connection host port)
(let* ((tcp (vector-ref (getproto "tcp") 2))
(addr (car (vector-ref (gethost host) 4)))
(sock (socket AF_INET SOCK_STREAM tcp)))
(connect sock AF_INET addr port)
(add-open-connection! host port sock)
sock))))
;;; (http:request METHOD URL [HEADERS [BODY]])
;;; Submit an HTTP request.
;;; URL is a structure returned by url:parse.
;;; METHOD is the name of some HTTP method, e.g. "GET" or "POST".
;;; The optional HEADERS and BODY arguments are lists of strings
;;; which describe HTTP messages. The `Content-Length' header
;;; is calculated automatically and should not be supplied.
;;;
;;; Example usage:
;;; (http:request "get" parsed-url
;;; (list "User-Agent: GuileHTTP 0.1"
;;; "Content-Type: text/plain"))
;;; (http:request "post" parsed-url
;;; (list "User-Agent: GuileHTTP 0.1"
;;; "Content-Type: unknown/x-www-form-urlencoded")
;;; (list "search=Gosper"
;;; "case=no"
;;; "max_hits=50"))
(define-public (http:request method url . args)
(let ((host (url:host url))
(tcp-port (or (url:port url) 80))
(path (string-append "/" (or (url:path url) ""))))
(let ((sock (http:open host tcp-port))
(request (string-append method " " path " " http:version))
(headers (if (pair? args) (car args) '()))
(body (if (and (pair? args) (pair? (cdr args)))
(cadr args)
'())))
(let* ((content-length
(apply +
(map (lambda (line)
(+ 2 (string-length line))) ; + 2 for CRLF
body)))
(headers (if (positive? content-length)
(cons (string-append "Content-Length: "
(number->string content-length))
headers)
headers)))
(with-output-to-port sock
(lambda ()
(display-with-crlf request)
(for-each display-with-crlf headers)
(display "\r\n")
(for-each display-with-crlf body)))
;; parse and add status line
;; also cons up a list of response headers
(let* ((response-status-line (sans-trailing-whitespace
(read-line sock 'trim)))
(response-headers
(let make-header-list ((ln (sans-trailing-whitespace
(read-line sock 'trim)))
(hlist '()))
(if (= 0 (string-length ln))
hlist
(make-header-list (sans-trailing-whitespace
(read-line sock 'trim))
(cons (http:header-parse ln)
hlist)))))
(response-status-fields
(parse-status-line response-status-line))
(response-version (car response-status-fields))
(response-code (cadr response-status-fields))
(response-text (caddr response-status-fields)))
;; signal error if HTTP status is invalid
;; (or (http:status-ok? response-code)
;; (error 'http-status "HTTP server returned bad status"
;; response-status-line))
;; Get message body: if Content-Length header was supplied, read
;; that many chars. Otherwise, read until EOF
(let ((content-length (http:fetch-header
"content-length"
response-headers)))
(let ((response-body
(if (and content-length
(not (string-ci=? method "HEAD")))
(read-n-chars (string->number content-length) sock)
(with-output-to-string
(lambda ()
(while (not (eof-object? (peek-char sock)))
(display (read-char sock))))))))
;; FIXME: what about keepalives?
(close-port sock)
(http:make-message response-version
response-code
response-text
response-headers
response-body))))))))
;;;; System interface cruft & string funcs
(define (read-n-chars num . port-arg)
(let ((p (if (null? port-arg)
(current-input-port)
(car port-arg)))
(s (make-string num)))
(do ((i 0 (+ i 1))
(ch (read-char p) (read-char p)))
((or (>= i num) (eof-object? ch)) s)
(string-set! s i ch))))
(define (display-with-crlf line . p)
(apply display line p)
(apply display "\r\n" p))
;;; (sans-trailing-whitespace STR)
;;; These are defined in module #/ice-9/string-fun, so this code
;;; will prob. be discarded when the module system and boot-9
;;; settle down.
(define (sans-trailing-whitespace s)
(let ((st 0)
(end (string-length s)))
(while (and (< 0 end)
(char-whitespace? (string-ref s (1- end))))
(set! end (1- end)))
(if (< end st)
""
(make-shared-substring s st end))))
+49
View File
@@ -0,0 +1,49 @@
;;;; www/main.scm: general WWW navigation aids.
(define-module (www main)
:use-module (www http)
:use-module (www url))
;;;; Copyright (C) 1997 Free Software Foundation, Inc.
;;;;
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2, or (at your option)
;;;; any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this software; see the file COPYING. If not, write to
;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
;;;; Boston, MA 02111-1307 USA
;;;;
(define dispatch-table
(acons 'http http:get '()))
;;; (www:get URL)
;;; parse a URL into portions, open a connection, and retrieve
;;; selected document
(define-public (www:set-protocol-handler! proto handler)
(set! dispatch-table
(assq-set! dispatch-table proto handler)))
(define-public (www:get url-str)
(let ((url (url:parse url-str)))
;; get handler for this protocol
(case (url:scheme url)
((http) (let ((msg (http:get url)))
(http:message-body msg)))
(else
(let ((handle (assq-ref dispatch-table (url:scheme url))))
(if handle
(handle (url:host url)
(url:port url)
(url:path url))
(error "unknown URL scheme" (url:scheme url))))))))
+152
View File
@@ -0,0 +1,152 @@
;;;; url.scm: URL manipulation tools.
(define-module (www url)
:use-module (ice-9 regex))
;;;; Copyright (C) 1997 Free Software Foundation, Inc.
;;;;
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2, or (at your option)
;;;; any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this software; see the file COPYING. If not, write to
;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
;;;; Boston, MA 02111-1307 USA
;;;;
;;;; TODO:
;;;; * support `user:password@' strings where appropriate in URLs.
;;;; * make URL parsing smarter. This is good for most TCP/IP-based
;;;; URL schemes, but parsing is actually specific to each URL scheme.
;;;; * fill out url:encode, include facilities for URL-scheme-specific
;;;; encoding methods (e.g. a url-scheme-reserved-char-alist)
;; `url:scheme' is an unfortunate term, but it is the technical
;; name for that portion of the URL according to RFC 1738. Sigh.
(define-public (url:scheme url) (vector-ref url 0))
(define-public (url:address url) (vector-ref url 1))
(define-public (url:unknown url) (vector-ref url 1))
(define-public (url:user url) (vector-ref url 1))
(define-public (url:host url) (vector-ref url 2))
(define-public (url:port url) (vector-ref url 3))
(define-public (url:path url) (vector-ref url 4))
(define-public (url:make scheme . args)
(apply vector scheme args))
(define-public (url:make-http host port path)
(vector 'http #f host port path))
(define-public (url:make-ftp user host port path)
(vector 'ftp user host port path))
(define-public (url:make-mailto address)
(vector 'mailto address))
(define http-regexp (make-regexp "^http://([^:/]+)(:([0-9]+))?(/(.*))?$"))
(define ftp-regexp
(make-regexp "^ftp://(([^@:/]+)@)?([^:/]+)(:([0-9]+))?(/(.*))?$"))
(define mailto-regexp (make-regexp "^mailto:(.*)$"))
(define-public (url:parse url)
(cond
((regexp-exec http-regexp url)
=> (lambda (m)
(url:make-http (match:substring m 1)
(cond ((match:substring m 3) => string->number)
(else #f))
(match:substring m 5))))
((regexp-exec ftp-regexp url)
=> (lambda (m)
(url:make-ftp (match:substring m 2)
(match:substring m 3)
(cond ((match:substring m 5) => string->number)
(else #f))
(match:substring m 7))))
((regexp-exec mailto-regexp url)
=> (lambda (m)
(url:make-mailto (match:substring m 1))))
(else
(url:make 'unknown url))))
(define-public (url:unparse url)
(define (pathy scheme username host port path)
(string-append (symbol->string scheme)
"://" host
(if port (string-append ":" (number->string port))
"")
(if path (string-append "/" path)
"")))
(case (url:scheme url)
((http) (pathy 'http #f
(url:host url)
(url:port url)
(url:path url)))
((ftp) (pathy 'ftp
(url:user url)
(url:host url)
(url:port url)
(url:path url)))
((mailto) (string-append "mailto:" (url:address url)))
((unknown) (url:unknown url))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (url-decode STR)
;; Turn + into space, and hex-encoded %XX strings into their
;; eight-bit characters. Is a regexp faster than character
;; scanning? Does it incur more overhead (which may be more
;; important for code that frequently gets restarted)?
(define-public (url:decode str)
(regexp-substitute/global #f "\\+|%([0-9A-Fa-f][0-9A-Fa-f])" str
'pre
(lambda (m)
(cond ((string=? "+" (match:substring m 0)) " ")
(else (integer->char
(string->number
(match:substring m 1)
16)))))
'post))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (url-encode STR)
;; The inverse of url-decode. Can't be done easily with
;; a regexp: we would have to construct a regular expression
;; like "[\277-\377]", for example, and Guile strings don't
;; let you interpolate character literals. Pity.
;; URL-encode any characters in STR that are not safe: these
;; include any character not in the SAFE-CHARS list and any
;; character that *is* in the RESERVED-CHARS list argument.
(define-public (url:encode str reserved-chars)
(with-output-to-string
(lambda ()
(for-each (lambda (ch)
(if (and (safe-char? ch)
(not (memv ch reserved-chars)))
(display ch)
(begin
(display #\%)
(display (number->string (char->integer ch) 16)))))
(string->list str)))))
(define safe-chars
'(#\$ #\- #\_ #\. #\+ #\! #\* #\' #\( #\) #\, #\; #\/ #\? #\: #\@ #\& #\=))
(define (safe-char? ch)
;; ``Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
;; reserved characters used for their reserved purposes may be used
;; unencoded within a URL.'' RFC 1738, #2.2.
(or (char-alphabetic? ch)
(char-numeric? ch)
(memv ch safe-chars)))
+28
View File
@@ -0,0 +1,28 @@
#!/usr/local/opt/guile-cvs/bin/guile -s
!#
;;;; wwwcat: a trivial `cat' program for WWW resources.
;;;;
;;;; Copyright (C) 1997 Free Software Foundation, Inc.
;;;;
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2, or (at your option)
;;;; any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this software; see the file COPYING. If not, write to
;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
;;;; Boston, MA 02111-1307 USA
;;;;
(use-modules (www main))
(if (program-arguments)
(display (www:get (list-ref (program-arguments) 1)))
(display "wwwget: no document specified" (current-error-port)))