use @verbatim{...}
svn: r8699
This commit is contained in:
parent
7e73623032
commit
9c77909710
|
@ -39,10 +39,9 @@ contain Scheme strings, and since escape characters are difficult for
|
|||
people to read, a @litchar{|} character in @scheme[sexpr] is
|
||||
converted to a @litchar{"} character before it is parsed. Thus,
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
<A MZSCHEME="|This goes nowhere.|">Nowhere</A>
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
creates a ``Nowhere'' hyperlink, which executes the Scheme program
|
||||
|
||||
|
@ -62,10 +61,9 @@ is executed immediately during HTML rendering. If the result is a
|
|||
string, the comment is replaced in the input HTML stream with the
|
||||
content of the string. Thus,
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
<!-- MZSCHEME="(format |<B>Here</B>: ~a| (current-directory))" -->
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
inserts the path of the current working directory into the containing
|
||||
document (and ``Here'' is boldfaced). If the result is a snip instead
|
||||
|
|
|
@ -24,31 +24,34 @@
|
|||
|
||||
@title{@bold{Handin Server}}
|
||||
|
||||
|
||||
@section{Handin-Server and Client}
|
||||
|
||||
The @scheme[handin-server] directory contains a server to be run by a course
|
||||
instructor for accepting homework assignments and reporting on
|
||||
The @scheme[handin-server] directory contains a server to be run by a
|
||||
course instructor for accepting homework assignments and reporting on
|
||||
submitted assignments.
|
||||
|
||||
The @scheme[handin-client] directory contains a client to be customized then
|
||||
re-distributed to students in the course. The customized client will embed a
|
||||
particular hostname and port where the server is running, as well as a server
|
||||
certificate.
|
||||
The @scheme[handin-client] directory contains a client to be
|
||||
customized then re-distributed to students in the course. The
|
||||
customized client will embed a particular hostname and port where the
|
||||
server is running, as well as a server certificate.
|
||||
|
||||
With a customized client, students simply install a @filepath{.plt} file---so
|
||||
there's no futzing with configuration dialogs and certificates. A student can
|
||||
install any number of clients at once (assuming that the clients are properly
|
||||
customized, as described below).
|
||||
With a customized client, students simply install a @filepath{.plt}
|
||||
file---so there's no futzing with configuration dialogs and
|
||||
certificates. A student can install any number of clients at once
|
||||
(assuming that the clients are properly customized, as described
|
||||
below).
|
||||
|
||||
The result, on the student's side, is a @onscreen{Handin} button in DrScheme's
|
||||
toolbar. Clicking the @onscreen{Handin} button allows the student to type a
|
||||
password and upload the current content of the definitions and interactions
|
||||
window to the course instructor's server. The @onscreen{File} menu is also
|
||||
extended with a @onscreen{Manage...} menu item for managing a handin account
|
||||
(i.e., changing the password and other information, or creating a new account
|
||||
if the instructor configures the server to allow new accounts). Students can
|
||||
submit joint work by submitting with a concatenation of usernames separated by
|
||||
a ``@tt{+}''.
|
||||
The result, on the student's side, is a @onscreen{Handin} button in
|
||||
DrScheme's toolbar. Clicking the @onscreen{Handin} button allows the
|
||||
student to type a password and upload the current content of the
|
||||
definitions and interactions window to the course instructor's server.
|
||||
The @onscreen{File} menu is also extended with a @onscreen{Manage...}
|
||||
menu item for managing a handin account (i.e., changing the password
|
||||
and other information, or creating a new account if the instructor
|
||||
configures the server to allow new accounts). Students can submit
|
||||
joint work by submitting with a concatenation of usernames separated
|
||||
by a ``@tt{+}''.
|
||||
|
||||
On the instructor's side, the handin server can be configured to check
|
||||
the student's submission before accepting it.
|
||||
|
@ -62,119 +65,124 @@ server and each user's password.
|
|||
@itemize{
|
||||
@item{Create a new directory.}
|
||||
|
||||
@item{Copy @filepath{server-cert.pem} from the @filepath{handin-client} collection
|
||||
to the new directory.
|
||||
|
||||
NOTE: For real use, you need a new certificate.
|
||||
|
||||
NOTE: See also @secref{wheres-the-collection}.}
|
||||
@item{Copy @filepath{server-cert.pem} from the
|
||||
@filepath{handin-client} collection to the new directory.
|
||||
|
||||
@item{Copy @filepath{private-key.pem} from the @filepath{handin-server} collection
|
||||
to the new directory.
|
||||
|
||||
NOTE: For real use, you need a new key.}
|
||||
NOTE: For real use, you need a new certificate.
|
||||
|
||||
NOTE: See also @secref{wheres-the-collection}.}
|
||||
|
||||
@item{Copy @filepath{private-key.pem} from the
|
||||
@filepath{handin-server} collection to the new directory.
|
||||
|
||||
NOTE: For real use, you need a new key.}
|
||||
|
||||
@item{Create a file @filepath{users.ss} with the following content:
|
||||
@schemeblock[
|
||||
((tester ("8fe4c11451281c094a6578e6ddbf5eed"
|
||||
"Tester" "1" "test@cs")))]}
|
||||
@schemeblock[
|
||||
((tester ("8fe4c11451281c094a6578e6ddbf5eed"
|
||||
"Tester" "1" "test@cs")))]}
|
||||
|
||||
@item{Make a @filepath{test} subdirectory in your new directory.}
|
||||
|
||||
@item{Create a file @filepath{config.ss} with the following content:
|
||||
@schemeblock[((active-dirs ("test")))]}
|
||||
@schemeblock[((active-dirs ("test")))]}
|
||||
|
||||
@item{In your new directory, run
|
||||
@commandline{mred -mvqM handin-server}}
|
||||
@item{In your new directory, run @commandline{mred -l handin-server}}
|
||||
|
||||
@item{In the @filepath{handin-client} collection, edit @filepath{info.ss} and
|
||||
uncomment the lines that define `server:port', @scheme[tools],
|
||||
@scheme[tool-names], and @scheme[tool-icons].}
|
||||
@item{In the @filepath{handin-client} collection, edit
|
||||
@filepath{info.ss} and uncomment the lines that define
|
||||
@scheme[server:port], @scheme[tools], @scheme[tool-names], and
|
||||
@scheme[tool-icons].}
|
||||
|
||||
@item{Run
|
||||
@commandline{setup-plt -l handin-client}
|
||||
@item{Run @commandline{setup-plt -l handin-client}
|
||||
|
||||
NOTE: Under Windows, the executable is ``@tt{Setup PLT}''
|
||||
instead of ``@tt{setup-plt}''.
|
||||
|
||||
NOTE: The command line arguments are optional.}
|
||||
NOTE: Under Windows, the executable is ``@tt{Setup PLT}'' instead of
|
||||
``@tt{setup-plt}''.
|
||||
|
||||
@item{Start DrScheme, click @onscreen{Handin} to run the client, submit with
|
||||
username ``@tt{tester}'' and password ``@tt{pw}''.
|
||||
NOTE: The command line arguments are optional.}
|
||||
|
||||
The submitted file will be @filepath{.../test/tester/handin.scm}.}
|
||||
@item{Start DrScheme, click @onscreen{Handin} to run the client,
|
||||
submit with username ``@tt{tester}'' and password ``@tt{pw}''.
|
||||
|
||||
The submitted file will be @filepath{.../test/tester/handin.scm}.}
|
||||
|
||||
@item{Check the status of your submission by pointing a web browser at
|
||||
@tt{https://localhost:7980/servlets/status.ss}.
|
||||
Note the ``s'' in ``https''. Use the ``@tt{tester}'' username and ``@tt{pw}''
|
||||
password, as before.}
|
||||
@tt{https://localhost:7980/servlets/status.ss}. Note the ``s'' in
|
||||
``https''. Use the ``@tt{tester}'' username and ``@tt{pw}''
|
||||
password, as before.}
|
||||
}
|
||||
|
||||
|
||||
@section[#:tag "wheres-the-collection"]{Where is the collection?}
|
||||
|
||||
If you obtained the server and client by installing a @filepath{.plt} file,
|
||||
then the @filepath{handin-server} and @filepath{handin-client} directories
|
||||
might be in your PLT addon space. Start MzScheme, and enter
|
||||
@schemeblock[ (collection-path "handin-server") ]
|
||||
@schemeblock[ (collection-path "handin-client") ]
|
||||
If you obtained the server and client by installing a @filepath{.plt}
|
||||
file, then the @filepath{handin-server} and @filepath{handin-client}
|
||||
directories might be in your PLT addon space. Start MzScheme, and
|
||||
enter
|
||||
@schemeblock[(collection-path "handin-server")]
|
||||
@schemeblock[(collection-path "handin-client")]
|
||||
to find out where these collections are.
|
||||
|
||||
|
||||
@section{Client Customization}
|
||||
|
||||
@itemize{
|
||||
@item{Rename (or make a copy of) the @filepath{handin-client} collection
|
||||
directory. The new name should describe your class uniquely.
|
||||
For example, @filepath{uu-cpsc2010} is a good name for CPSC 2010 at the
|
||||
University of Utah.}
|
||||
@item{Rename (or make a copy of) the @filepath{handin-client}
|
||||
collection directory. The new name should describe your class
|
||||
uniquely. For example, @filepath{uu-cpsc2010} is a good name for CPSC
|
||||
2010 at the University of Utah.}
|
||||
|
||||
@item{Edit the first three definitions of @filepath{info.ss} in your renamed
|
||||
client collection:
|
||||
@itemize{
|
||||
@item{For @scheme[name], choose a name for the handin tool as it will
|
||||
appear in DrScheme's interface (e.g., the @onscreen{XXX} for the
|
||||
@onscreen{Manage XXX Handin Account...} menu item). Again, make the
|
||||
name specific to the course, in case a student installs
|
||||
multiple handin tools. Do not use @onscreen{Handin} as the last part
|
||||
of the name, since @onscreen{Handin} is always added for button and
|
||||
menu names.}
|
||||
@item{Edit the first three definitions of @filepath{info.ss} in your
|
||||
renamed client collection:
|
||||
@itemize{
|
||||
@item{For @scheme[name], choose a name for the handin tool as it
|
||||
will appear in DrScheme's interface (e.g., the @onscreen{XXX} for
|
||||
the @onscreen{Manage XXX Handin Account...} menu item). Again,
|
||||
make the name specific to the course, in case a student installs
|
||||
multiple handin tools. Do not use @onscreen{Handin} as the last
|
||||
part of the name, since @onscreen{Handin} is always added for
|
||||
button and menu names.}
|
||||
|
||||
@item{Uncomment the definitions of @scheme[tools],
|
||||
@scheme[tool-names], and @scheme[tool-icons].}
|
||||
@item{Uncomment the definitions of @scheme[tools],
|
||||
@scheme[tool-names], and @scheme[tool-icons].}
|
||||
|
||||
@item{For @scheme[server:port], uncomment the line, and use the hostname
|
||||
and port where the server will be running to accept handin
|
||||
submissions.}
|
||||
}
|
||||
Optionally uncomment and edit the next two definitions,
|
||||
@scheme[web-menu-name] and @scheme[web-address], to add an item to the @onscreen{Help}
|
||||
menu that opens a (course-specific) web page.}
|
||||
@item{For @scheme[server:port], uncomment the line, and use the
|
||||
hostname and port where the server will be running to accept
|
||||
handin submissions.}}
|
||||
|
||||
@item{ Replace @filepath{icon.png} in your renamed directory with a new 32x32
|
||||
icon. This icon is displayed on startup with DrScheme's splash
|
||||
screen, and it is included at half size on the @onscreen{Handin} button.
|
||||
Again, choose a distinct icon for the benefit of students who
|
||||
install multiple handin tools. A school logo is typically
|
||||
useful, as it provides a recognizably local visual cue.}
|
||||
Optionally uncomment and edit the next two definitions,
|
||||
@scheme[web-menu-name] and @scheme[web-address], to add an item to
|
||||
the @onscreen{Help} menu that opens a (course-specific) web page.}
|
||||
|
||||
@item{ Replace @filepath{server-cert.pem} in your renamed directory with a
|
||||
server certificate. The file @filepath{server-cert.pem} in
|
||||
@filepath{handin-client} collection is ok for testing, but the point of
|
||||
this certificate is to make handins secure, so you should
|
||||
generate a new (self-certifying) certificate and keep its key
|
||||
private. (See @secref{server-setup}.)}
|
||||
@item{Replace @filepath{icon.png} in your renamed directory with a new
|
||||
32x32 icon. This icon is displayed on startup with DrScheme's
|
||||
splash screen, and it is included at half size on the
|
||||
@onscreen{Handin} button. Again, choose a distinct icon for the
|
||||
benefit of students who install multiple handin tools. A school
|
||||
logo is typically useful, as it provides a recognizably local visual
|
||||
cue.}
|
||||
|
||||
@item{ Run
|
||||
@commandline{mzc --collection-plt <name>.plt <name>}
|
||||
where @tt{<name>} is the name that you chose for your directory
|
||||
(i.e., whatever you changed @filepath{handin-client} to).}
|
||||
@item{Replace @filepath{server-cert.pem} in your renamed directory
|
||||
with a server certificate. The file @filepath{server-cert.pem} in
|
||||
@filepath{handin-client} collection is ok for testing, but the point
|
||||
of this certificate is to make handins secure, so you should
|
||||
generate a new (self-certifying) certificate and keep its key
|
||||
private. (See @secref{server-setup}.)}
|
||||
|
||||
@item{Run @commandline{mzc --collection-plt <name>.plt <name>} where
|
||||
@tt{<name>} is the name that you chose for your directory (i.e.,
|
||||
whatever you changed @filepath{handin-client} to).}
|
||||
|
||||
@item{Distribute @filepath{<name>.plt} to students for installation
|
||||
into their copies of DrScheme. The students need not have access to
|
||||
the DrScheme installation directory; the tool will be installed on
|
||||
the filesystem in the student's personal space. If you want to
|
||||
install it once on a shared installation, use setup-plt with the
|
||||
@DFlag{all-users} flag.}
|
||||
|
||||
@item{ Distribute @filepath{<name>.plt} to students for installation into their
|
||||
copies of DrScheme. The students need not have access to the
|
||||
DrScheme installation directory; the tool will be installed on
|
||||
the filesystem in the student's personal space. If you want to
|
||||
install it once on a shared installation, use setup-plt with the
|
||||
@DFlag{all-users} flag.}
|
||||
}
|
||||
|
||||
|
||||
@section[#:tag "server-setup"]{Server Setup}
|
||||
|
||||
You must prepare a special directory to host the handin server. To
|
||||
|
@ -183,362 +191,353 @@ set the @envvar{PLT_HANDINSERVER_DIR} environment variable.
|
|||
|
||||
This directory contains the following files and sub-directories:
|
||||
@itemize{
|
||||
@item{@filepath{server-cert.pem} : the server's certificate. To create a
|
||||
certificate and key with openssl:
|
||||
@item{@filepath{server-cert.pem}: the server's certificate. To create
|
||||
a certificate and key with openssl:
|
||||
@commandline{openssl req -new -nodes -x509 -days 365
|
||||
-out server-cert.pem -keyout private-key.pem}}
|
||||
|
||||
@commandline{openssl req -new -nodes -x509 -days 365 -out server-cert.pem -keyout private-key.pem}
|
||||
}
|
||||
@item{@filepath{private-key.pem} : the private key to go with
|
||||
@filepath{server-cert.pem}. Whereas @filepath{server-cert.pem} gets distributed to
|
||||
students with the handin client, @filepath{private-key.pem} is kept
|
||||
private.}
|
||||
@item{@filepath{private-key.pem}: the private key to go with
|
||||
@filepath{server-cert.pem}. Whereas @filepath{server-cert.pem} gets
|
||||
distributed to students with the handin client,
|
||||
@filepath{private-key.pem} is kept private.}
|
||||
|
||||
@item{@filepath{config.ss} : configuration options. The file format is
|
||||
@item{@filepath{config.ss}: configuration options. The file format is
|
||||
@schemeblock[((<key> <val>) ...)]
|
||||
|
||||
@schemeblock[ ((<key> <val>) ...)]
|
||||
The following keys can be used:
|
||||
|
||||
The following keys can be used:
|
||||
@itemize{
|
||||
@item{@indexed-scheme[active-dirs] --- a list of directories that
|
||||
are active submissions, relative to the current directory or
|
||||
absolute; the last path element for each of these (and
|
||||
@scheme[inactive-dirs] below) should be unique, and is used to
|
||||
identify the submission (for example, in the client's submission
|
||||
dialog and in the status servlet).}
|
||||
|
||||
|
||||
@comment{I took out the quote before each identifier.}
|
||||
|
||||
@itemize{
|
||||
@item{@indexed-scheme[active-dirs] --- a list of directories that are active
|
||||
submissions, relative to the current directory or absolute;
|
||||
the last path element for each of these (and @scheme['inactive-dirs]
|
||||
below) should be unique, and is used to identify the
|
||||
submission (for example, in the client's submission dialog
|
||||
and in the status servlet).}
|
||||
@item{@indexed-scheme[inactive-dirs] --- a list of inactive
|
||||
submission directories (see above for details).}
|
||||
|
||||
@item{@indexed-scheme[inactive-dirs] --- a list of inactive submission directories (see
|
||||
above for details).}
|
||||
@item{@indexed-scheme[port-number] --- the port for the main handin
|
||||
server; the default is 7979.}
|
||||
|
||||
@item{@indexed-scheme[port-number] --- the port for the main handin server; the default
|
||||
is 7979.}
|
||||
@item{@indexed-scheme[https-port-number] --- the port number for the
|
||||
handin-status HTTPS server; the default is @scheme[#f] which
|
||||
indicates that no HTTPS server is started.}
|
||||
|
||||
@item{@indexed-scheme[https-port-number] --- the port number for the handin-status HTTPS
|
||||
server; the default is @scheme[#f] which indicates that no HTTPS
|
||||
server is started.}
|
||||
@item{@indexed-scheme[session-timeout] --- number of seconds before
|
||||
the session times-out. The client is given this many seconds for
|
||||
the login stage and then starts again so the same number of
|
||||
seconds is given for the submit-validation process; the default is
|
||||
300.}
|
||||
|
||||
@item{@indexed-scheme[session-timeout] --- number of seconds before the session
|
||||
times-out. The client is given this many seconds for the
|
||||
login stage and then starts again so the same number of
|
||||
seconds is given for the submit-validation process; the
|
||||
default is 300.}
|
||||
@item{@indexed-scheme[session-memory-limit] --- maximum size in
|
||||
bytes of memory allowed for per-session computation, if
|
||||
per-session limits are supported (i.e., when using MrEd and
|
||||
MzScheme with the (default) exact garbage collector and memory
|
||||
accounting); the default is 40000000.}
|
||||
|
||||
@item{@indexed-scheme[session-memory-limit] --- maximum size in bytes of memory allowed
|
||||
for per-session computation, if per-session limits are
|
||||
supported (i.e., when using MrEd3m and MzScheme3m with memory
|
||||
accounting); the default is 40000000.}
|
||||
@item{@indexed-scheme[default-file-name] --- the default filename
|
||||
that will be saved with the submission contents. The default is
|
||||
@filepath{handin.scm}.}
|
||||
|
||||
@item{@indexed-scheme[default-file-name] --- the default filename that will be saved
|
||||
with the submission contents. The default is @filepath{handin.scm}.}
|
||||
@item{@indexed-scheme[max-upload] --- maximum size in bytes of an
|
||||
acceptable submission; the default is 500000.}
|
||||
|
||||
@item{@indexed-scheme[max-upload] --- maximum size in bytes of an acceptable submission;
|
||||
the default is 500000.}
|
||||
@item{@indexed-scheme[max-upload-keep] --- maximum index of
|
||||
submissions to keep; the most recent submission is
|
||||
@filepath{handin.scm} (by default), the next oldest is in
|
||||
@filepath{BACKUP-0/handin.scm}, next oldest is
|
||||
@filepath{BACKUP-1/handin.scm}, etc. The default is 9.}
|
||||
|
||||
@item{@indexed-scheme[max-upload-keep] --- maximum index of submissions to keep; the
|
||||
most recent submission is @filepath{handin.scm} (by default), the next
|
||||
oldest is in @filepath{BACKUP-0/handin.scm}, next oldest is
|
||||
@filepath{BACKUP-1/handin.scm}, etc. The default is 9.}
|
||||
@item{@indexed-scheme[user-regexp] --- a regular expression that is
|
||||
used to validate usernames; alternatively, this can be @scheme[#f]
|
||||
meaning no restriction, or a list of permitted strings. Young
|
||||
students often choose exotic usernames that are impossible to
|
||||
remember, and forget capitalization, so the default is fairly
|
||||
strict--- @scheme[#rx"^[a-z][a-z0-9]+$"]; a @scheme["+"] is always
|
||||
disallowed in a username, since it is used in a submission
|
||||
username to specify joint work.}
|
||||
|
||||
@item{@indexed-scheme[user-regexp] --- a regular expression that is used to validate usernames;
|
||||
alternatively, this can be @scheme[#f] meaning no restriction, or a list of
|
||||
permitted strings. Young students often choose exotic usernames that
|
||||
are impossible to remember, and forget capitalization, so the default
|
||||
is fairly strict--- @scheme[#rx"^[a-z][a-z0-9]+$"]; a @scheme["+"] is
|
||||
always disallowed in a username, since it is used in a submission
|
||||
username to specify joint work.}
|
||||
@item{@indexed-scheme[user-desc] --- a plain-words description of
|
||||
the acceptable username format (according to user-regexp above);
|
||||
@scheme[#f] stands for no description; the default is
|
||||
@scheme["alphanumeric string"] which matches the default
|
||||
user-regexp.}
|
||||
|
||||
@item{@indexed-scheme[user-desc] --- a plain-words description of the acceptable
|
||||
username format (according to user-regexp above); @scheme[#f] stands
|
||||
for no description; the default is @scheme["alphanumeric string"]
|
||||
which matches the default user-regexp.}
|
||||
@item{@indexed-scheme[username-case-sensitive] --- a boolean; when
|
||||
@scheme[#f], usernames are case-folded for all purposes; defaults
|
||||
to @scheme[#f] (note that you should not set this to @scheme[#t]
|
||||
on Windows or when using other case-insensitive filesystems, since
|
||||
usernames are used as directory names).}
|
||||
|
||||
@item{@indexed-scheme[username-case-sensitive] --- a boolean; when @scheme[#f], usernames are
|
||||
case-folded for all purposes; defaults to @scheme[#f] (note that you
|
||||
should not set this to @scheme[#t] on Windows or when using other
|
||||
case-insensitive filesystems, since usernames are used as
|
||||
directory names).}
|
||||
@item{@indexed-scheme[allow-new-users] --- a boolean indicating
|
||||
whether to allow new-user requests from a client tool; the default
|
||||
is @scheme[#f].}
|
||||
|
||||
@item{@indexed-scheme[allow-new-users] --- a boolean indicating whether to allow
|
||||
new-user requests from a client tool; the default is @scheme[#f].}
|
||||
@item{@indexed-scheme[allow-change-info] --- a boolean indicating
|
||||
whether to allow changing user information from a client tool
|
||||
(changing passwords is always possible); the default is
|
||||
@scheme[#f].}
|
||||
|
||||
@item{@indexed-scheme[allow-change-info] --- a boolean indicating whether to allow
|
||||
changing user information from a client tool (changing
|
||||
passwords is always possible); the default is @scheme[#f].}
|
||||
@item{@indexed-scheme[master-password] --- a string for an MD5 hash
|
||||
for a password that allows login as any user; the default is
|
||||
@scheme[#f], which disables the password.}
|
||||
|
||||
@item{@indexed-scheme[master-password] --- a string for an MD5 hash for a password that
|
||||
allows login as any user; the default is @scheme[#f], which disables
|
||||
the password.}
|
||||
@item{@indexed-scheme[log-output] --- a boolean that controls
|
||||
whether the handin server log is written on the standard output;
|
||||
defaults to @scheme[#t].}
|
||||
|
||||
@item{@indexed-scheme[log-output] --- a boolean that controls whether the handin server
|
||||
log is written on the standard output; defaults to @scheme[#t].}
|
||||
@item{@indexed-scheme[log-file] --- a path (relative to handin
|
||||
server directory or absolute) that specifies a filename for the
|
||||
handin server log (possibly combined with the @scheme[log-output]
|
||||
option), or @scheme[#f] for no log file; defaults to
|
||||
@filepath{log}.}
|
||||
|
||||
@item{@indexed-scheme[log-file] --- a path (relative to handin server directory or
|
||||
absolute) that specifies a filename for the handin server log
|
||||
(possibly combined with the 'log-output option), or @scheme[#f] for
|
||||
no log file; defaults to @filepath{log}.}
|
||||
@item{@indexed-scheme[web-base-dir] --- if @scheme[#f] (the
|
||||
default), the built-in web server will use the
|
||||
@filepath{status-web-root} in this collection for its
|
||||
configuration; to have complete control over the built in server,
|
||||
you can copy and edit @filepath{status-web-root}, and add this
|
||||
configuration entry with the name of your new copy (relative to
|
||||
the handin server directory, or absolute).}
|
||||
|
||||
@item{@indexed-scheme[web-base-dir] --- if @scheme[#f] (the default), the built-in web server
|
||||
will use the @filepath{status-web-root} in this collection for its
|
||||
configuration; to have complete control over the built in
|
||||
server, you can copy and edit @filepath{status-web-root}, and add this
|
||||
configuration entry with the name of your new copy (relative
|
||||
to the handin server directory, or absolute).}
|
||||
@item{@indexed-scheme[web-log-file] --- a path (relative to handin
|
||||
server directory or absolute) that specifies a filename for
|
||||
logging the internal HTTPS status web server; or @scheme[#f] (the
|
||||
default) to disable this log.}
|
||||
|
||||
@item{@indexed-scheme[web-log-file] --- a path (relative to handin server directory or
|
||||
absolute) that specifies a filename for logging the internal
|
||||
HTTPS status web server; or @scheme[#f] (the default) to disable
|
||||
this log.}
|
||||
@item{@indexed-scheme[extra-fields] --- a list that describes extra
|
||||
string fields of information for student records; each element in
|
||||
this list is a list of three values: the name of the field, the
|
||||
regexp (or @scheme[#f], or a list of permitted string values), and
|
||||
a string describing acceptable strings. The default is
|
||||
@schemeblock[
|
||||
'(("Full Name" #f #f)
|
||||
("ID#" #f #f)
|
||||
("Email" #rx"^[^@<>\"`',]+@[a-zA-Z0-9_.-]+[.][a-zA-Z]+$"
|
||||
"a valid email address"))]
|
||||
You can set this to a list of fields that you are interested in
|
||||
keeping, for example:
|
||||
@schemeblock[
|
||||
'(("Full Name"
|
||||
#rx"^[A-Z][a-zA-Z]+(?: [A-Z][a-zA-Z]+)+$"
|
||||
"full name, no punctuations, properly capitalized")
|
||||
("Utah ID Number"
|
||||
#rx"^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$"
|
||||
"Utah ID Number with exactly nine digits")
|
||||
("Email"
|
||||
#rx"^[^@<>\"`',]+@cs\\.utah\\.edu$"
|
||||
"A Utah CS email address"))]
|
||||
The order of these fields will be used both on the client GUI side
|
||||
and in the @filepath{users.ss} file (see below).
|
||||
|
||||
@item{@indexed-scheme[extra-fields] --- a list that describes extra string fields of
|
||||
information for student records; each element in this list is
|
||||
a list of three values -- the name of the field, the regexp
|
||||
(or @scheme[#f], or a list of permitted string values), and a string
|
||||
describing of acceptable strings. The default is
|
||||
@schemeblock[
|
||||
'(("Full Name" #f #f)
|
||||
("ID#" #f #f)
|
||||
("Email" #rx"^[^@<>\"`',]+@[a-zA-Z0-9_.-]+[.][a-zA-Z]+$"
|
||||
"a valid email address"))]
|
||||
@comment{a hyperlink here for users.ss?}
|
||||
|
||||
You can set this to a list of fields that you are interested
|
||||
in keeping, for example:
|
||||
The second item in a field description can also be the symbol
|
||||
@scheme['-], which marks this field as one that is hidden from the
|
||||
user interface: students will not see it and will not be able to
|
||||
provide or modify it; when a new student creates an account, such
|
||||
fields will be left empty. This is useful for adding information
|
||||
that you have on students from another source, for example, adding
|
||||
information from a course roster. You should manually edit the
|
||||
@filepath{users.ss} file and fill in such information. (The third
|
||||
element for such descriptors is ignored.)}
|
||||
|
||||
@schemeblock[
|
||||
'(("Full Name"
|
||||
#rx"^[A-Z][a-zA-Z]+(?: [A-Z][a-zA-Z]+)+$"
|
||||
"full name, no punctuations, properly capitalized")
|
||||
("Utah ID Number"
|
||||
#rx"^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$"
|
||||
"Utah ID Number with exactly nine digits")
|
||||
("Email"
|
||||
#rx"^[^@<>\"`',]+@cs\\.utah\\.edu$"
|
||||
"A Utah CS email address"))]
|
||||
@item{@indexed-scheme[hook-file] --- a path (relative to handin
|
||||
server directory or absolute) that specifies a filename that
|
||||
contains a `hook' module. This is useful as a general device for
|
||||
customizing the server through Scheme code. The file is expected
|
||||
to contain a module that provides a @scheme[hook] function, which
|
||||
should be receiving three arguments:
|
||||
|
||||
The order of these fields will be used both on the client GUI
|
||||
side and in the @filepath{users.ss} file (see below).
|
||||
@declare-exporting[#:use-sources (handin-server/scribblings/hook-dummy)]
|
||||
|
||||
@comment{a hyperlink here for users.ss?}
|
||||
@defproc[(hook [operation symbol?]
|
||||
[connection-context (or/c number? symbol? false?)]
|
||||
[relevant-info (listof (list/c symbol? any))])
|
||||
void?]{
|
||||
|
||||
The second item in a field description can also be the symbol
|
||||
@scheme['-], which marks this field as one that is hidden from the
|
||||
user interface: students will not see it and will not be able
|
||||
to provide or modify it; when a new student creates an
|
||||
account, such fields will be left empty. This is useful for
|
||||
adding information that you have on students from another
|
||||
source, for example, adding information from a course roster.
|
||||
You should manually edit the @filepath{users.ss} file and fill in such
|
||||
information. (The third element for such descriptors is
|
||||
ignored.)}
|
||||
The @scheme[operation] argument indicates the operation that is
|
||||
now taking place. It can be one of the following:
|
||||
@indexed-scheme['server-start],
|
||||
@indexed-scheme['server-connect], @indexed-scheme['user-create],
|
||||
@indexed-scheme['user-change], @indexed-scheme['login],
|
||||
@indexed-scheme['submission-received],
|
||||
@indexed-scheme['submission-committed],
|
||||
@indexed-scheme['submission-retrieved],
|
||||
@indexed-scheme['status-login], or
|
||||
@indexed-scheme['status-file-get].
|
||||
|
||||
@item{@indexed-scheme[hook-file] --- a path (relative to handin server directory or
|
||||
absolute) that specifies a filename that contains a `hook'
|
||||
module. This is useful as a general device for customizing
|
||||
the server through Scheme code. The file is expected to
|
||||
contain a module that provides a @scheme[hook] function, which
|
||||
should be receiving three arguments:
|
||||
The @scheme[connection-context] argument is a datum that
|
||||
specifies the connection context (a number for handin
|
||||
connections, a @scheme['wN] symbol for servlet connections, and
|
||||
@scheme[#f] for other server operations).
|
||||
|
||||
The @scheme[relevant-info] contains an alist of information
|
||||
relevant to this operation. Currently, the hook is used in
|
||||
several places after an operation has completed.
|
||||
|
||||
For example, here is a simple hook module that sends
|
||||
notification messages when users are created or their
|
||||
information has changed:
|
||||
|
||||
@declare-exporting[#:use-sources (handin-server/scribblings/hook-dummy)]
|
||||
@schememod[
|
||||
mzscheme
|
||||
(provide hook)
|
||||
(require (lib "sendmail.ss" "net"))
|
||||
(define (hook what session alist)
|
||||
(when (memq what '(user-create user-change))
|
||||
(send-mail-message
|
||||
"course-staff@university.edu"
|
||||
(format "[server] ~a (~a)" what session)
|
||||
'("course-staff@university.edu") '() '()
|
||||
(map (lambda (key+val) (apply format "~a: ~s" key+val))
|
||||
alist))))]}}}
|
||||
|
||||
@comment{should the result be void?}
|
||||
@defproc[(hook [operation symbol?] [connection-context (or/c number? symbol? false?)]
|
||||
[relevant-info (listof (list/c symbol? any))])
|
||||
any/c]{
|
||||
Changes to @filepath{config.ss} are detected, the file will be
|
||||
re-read, and options are reloaded. A few options are fixed at
|
||||
startup time: port numbers, log file specs, and the
|
||||
@scheme[web-base-dir] are as configured at startup. All other
|
||||
options will change the behavior of the running server (but things
|
||||
like @scheme[username-case-sensitive?] it would be unwise to do
|
||||
so). (For safety, options are not reloaded until the file parses
|
||||
correctly, but make sure that you don't save a copy that has
|
||||
inconsistent options: it is best to create a new configuration file
|
||||
and move it over the old one, or use an editor that does so and not
|
||||
save until the new contents is ready.) This is most useful for
|
||||
closing & opening submissions directories.}
|
||||
|
||||
The @scheme[operation] argument indicates the operation that is now
|
||||
taking place. It can be one of the following:
|
||||
@indexed-scheme['server-start], @indexed-scheme['server-connect],
|
||||
@indexed-scheme['user-create], @indexed-scheme['user-change], @indexed-scheme['login],
|
||||
@indexed-scheme['submission-received], @indexed-scheme['submission-committed],
|
||||
@indexed-scheme['submission-retrieved], @indexed-scheme['status-login],
|
||||
or @indexed-scheme['status-file-get].
|
||||
@item{@filepath{users.ss} (created if not present if a user is added):
|
||||
keeps the list of user accounts, along with the associated password
|
||||
(actually the MD5 hash of the password), and extra string fields as
|
||||
specified by the 'extra-fields configuration entry (in the same
|
||||
order). The file format is
|
||||
@schemeblock{
|
||||
((<username-sym> (<pw-md5-str> <extra-field> ...))
|
||||
...)}
|
||||
|
||||
The @scheme[connection-context] argument is a datum
|
||||
that specifies the connection context (a number for handin
|
||||
connections, a @scheme['wN] symbol for servlet connections,
|
||||
and @scheme[#f] for other server operations).
|
||||
|
||||
The @scheme[relevant-info] contains an alist of information relevant
|
||||
to this operation. Currently, the hook is used in several places
|
||||
after an operation has completed.
|
||||
For example, the default @scheme['extra-field] setting will make this:
|
||||
@schemeblock{
|
||||
((<username-sym> (<pw-md5-str> <full-name> <id> <email>))
|
||||
...)}
|
||||
|
||||
For example, here is a simple hook module that sends
|
||||
notification messages when users are created or their information has
|
||||
changed:
|
||||
Usernames that begin with ``solution'' are special. They are used by the
|
||||
HTTPS status server. Independent of the @scheme['user-regexp] and
|
||||
@scheme['username-case-sensitive?] configuration items, usernames are not
|
||||
allowed to contain characters that are illegal in Windows pathnames, and they
|
||||
cannot end or begin in spaces or periods.
|
||||
|
||||
@schemeblock[
|
||||
(module hook mzscheme
|
||||
(provide hook)
|
||||
(require (lib "sendmail.ss" "net"))
|
||||
(define (hook what session alist)
|
||||
(when (memq what '(user-create user-change))
|
||||
(send-mail-message
|
||||
"course-staff@university.edu"
|
||||
(format "[server] ~a (~a)" what session)
|
||||
'("course-staff@university.edu") '() '()
|
||||
(map (lambda (key+val)
|
||||
(apply format "~a: ~s" key+val))
|
||||
alist)))))]
|
||||
If the @scheme['allow-new-users] configuration allows new users, the
|
||||
@filepath{users.ss} file can be updated by the server with new
|
||||
users. It can always be updated by the server to change passwords.
|
||||
|
||||
Changes to @filepath{config.ss} are detected, the file will be re-read, and
|
||||
options are reloaded. A few options are fixed at startup time:
|
||||
port numbers, log file specs, and the @scheme[web-base-dir] are as
|
||||
configured at startup. All other options will change the behavior
|
||||
of the running server (but things like @scheme[username-case-sensitive?]
|
||||
it would be unwise to do so). (For safety, options are not
|
||||
reloaded until the file parses correctly, but make sure that you
|
||||
don't save a copy that has inconsistent options: it is best to
|
||||
create a new configuration file and move it over the old one, or
|
||||
use an editor that does so and not save until the new contents is
|
||||
ready.) This is most useful for closing & opening submissions
|
||||
directories.}}}
|
||||
}
|
||||
@item{@filepath{users.ss} (created if not present if a user is added) : keeps
|
||||
the list of user accounts, along with the associated password
|
||||
(actually the MD5 hash of the password), and extra string fields
|
||||
as specified by the 'extra-fields configuration entry (in the same
|
||||
order). The file format is
|
||||
If you have access to a standard Unix password file (from
|
||||
@filepath{/etc/passwd} or @filepath{/etc/shadow}), then you can
|
||||
construct a @filepath{users.ss} file that will allow users to use
|
||||
their normal passwords. To achieve this, use a list with 'unix as
|
||||
the first element and the system's encrypted password string as the
|
||||
second element. Such passwords can be used, but when users change
|
||||
them, a plain md5 hash will be used.
|
||||
|
||||
@schemeblock{
|
||||
((<username-sym> (<pw-md5-str> <extra-field> ...))
|
||||
...)
|
||||
}
|
||||
You can combine this with other fields from the password file to
|
||||
create your @filepath{users.ss}, but make sure you have information
|
||||
that matches your 'extra-fields specification. For example, given
|
||||
this system file:
|
||||
@verbatim{
|
||||
foo:wRzN1u5q2SqRD:1203:1203:L.E. Foo :/home/foo:/bin/tcsh
|
||||
bar:$1$dKlU0OkJ$t63TzKz:1205:1205:Bar Z. Lie:/home/bar:/bin/bash}
|
||||
you can create this @filepath{users.ss} file:
|
||||
@schemeblock[
|
||||
((foo ((unix "wRzN1u5q2SqRD") "L.E. Foo" "?"))
|
||||
(bar ((unix "$1$dKlU0OkJ$t63TzKz") "Bar Z. Lie" "?")))]
|
||||
which can be combined with this setting for @scheme['extra-fields]
|
||||
in your @filepath{config.ss}:
|
||||
@schemeblock[
|
||||
...
|
||||
(extra-fields (("Full Name" #f #f)
|
||||
("TA" '("Alice" "Bob") "Your TA")))
|
||||
...]
|
||||
and you can tell your students to use their department username and
|
||||
password, and use the @onscreen{Manage ...} dialog to properly set
|
||||
their TA name.
|
||||
|
||||
For example, the default @scheme['extra-field] setting will make this:
|
||||
Finally, a password value can be a list that begins with a
|
||||
@scheme['plaintext] symbol, which will be used without encryption.
|
||||
This may be useful for manually resetting a forgotten passwords.}
|
||||
|
||||
@schemeblock{
|
||||
((<username-sym> (<pw-md5-str> <full-name> <id> <email>))
|
||||
...)
|
||||
}
|
||||
@item{@filepath{log} (or any other name that the @scheme['log-file]
|
||||
configuration option specifies (if any), created if not present,
|
||||
appended otherwise): records connections and actions, where each
|
||||
entry is of the form
|
||||
@verbatim{[<id>|<time>] <msg>}
|
||||
where @scheme[<id>] is an integer representing the connection
|
||||
(numbered consecutively from 1 when the server starts), ``@tt{-}''
|
||||
for a message without a connection, and ``@tt{wN}'' for a message
|
||||
from the status servlet.}
|
||||
|
||||
Usernames that begin with ``solution'' are special. They are used by the
|
||||
HTTPS status server. Independent of the @scheme['user-regexp] and
|
||||
@scheme['username-case-sensitive?] configuration items, usernames are not
|
||||
allowed to contain characters that are illegal in Windows pathnames, and they
|
||||
cannot end or begin in spaces or periods.
|
||||
@item{Active and inactive assignment directories (which you can put in
|
||||
a nested directory for convenience, or specify a different absolute
|
||||
directory), as specified by the configuration file using the
|
||||
@scheme['active-dirs] and @scheme['inactive-dirs]. A list of active
|
||||
assignment directories (the last path element in each specified path
|
||||
is used as a label) is sent to the client tool when a student clicks
|
||||
@onscreen{Handin}. The assignment labels are ordered in the
|
||||
student's menu using @scheme[string<?], and the first assignment is
|
||||
the default selection.
|
||||
|
||||
If the @scheme['allow-new-users] configuration allows new users, the
|
||||
@filepath{users.ss} file can be updated by the server with new users. It can
|
||||
always be updated by the server to change passwords.
|
||||
Within each assignment directory, the student id is used for a
|
||||
sub-directory name. Within each student sub-directory are
|
||||
directories for handin attempts and successes. If a directory
|
||||
@filepath{ATTEMPT} exists, it contains the most recent (unsuccessful
|
||||
or currently-in-submission) handin attempt. Directories
|
||||
@filepath{SUCCESS-n} (where n counts from 0) contain successful
|
||||
handins; the lowest numbered such directory represents the latest
|
||||
handin.
|
||||
|
||||
If you have access to a standard Unix password file (from
|
||||
@filepath{/etc/passwd} or @filepath{/etc/shadow}), then you can construct a
|
||||
@filepath{users.ss} file that will allow users to use their normal
|
||||
passwords. To achieve this, use a list with 'unix as the first
|
||||
element and the system's encrypted password string as the second
|
||||
element. Such passwords can be used, but when users change them,
|
||||
a plain md5 hash will be used.
|
||||
A cleanup process in the server copies successful submissions to the
|
||||
student directory, one level up from the corresponding
|
||||
@filepath{SUCCESS-n} directory. This is done only for files and
|
||||
directories that are newer in @filepath{SUCCESS-n} than in the
|
||||
submission root, other files and directories are left intact. If
|
||||
external tools add new content to the student directory (e.g., a
|
||||
@filepath{grade} file, as described below) it will stay there. If
|
||||
the machine crashes or the server is stopped, the cleanup process
|
||||
might not finish. When the server is started, it automatically runs
|
||||
the cleanup process for each student directory.
|
||||
|
||||
You can combine this with other fields from the password file to
|
||||
create your @filepath{users.ss}, but make sure you have information that
|
||||
matches your 'extra-fields specification. For example, given this
|
||||
system file:
|
||||
Within a student directory, a @filepath{handin.scm} file (or some
|
||||
other name if the @scheme[default-file-name] option is set) contains
|
||||
the actual submission. A @scheme[checker] procedure can change this
|
||||
default file name, and it can create additional files in an
|
||||
@filepath{ATTEMPT} directory (to be copied by the cleanup process);
|
||||
see below for more details on @schememodname[handin-server/checker].
|
||||
|
||||
@verbatim[
|
||||
"foo:wRzN1u5q2SqRD:1203:1203:L.E. Foo:/home/foo:/bin/tcsh
|
||||
bar:$1$dKlU0OkJ$t63TzKz:1205:1205:Bar Z. Lie:/home/bar:/bin/bash"]
|
||||
For submissions from a normal DrScheme frame, a submission file
|
||||
contains a copy of the student's definitions and interactions
|
||||
windows. The file is in a binary format (to support non-text code),
|
||||
and opening the file directly in DrScheme shows the definitions
|
||||
part. To get both the definitions and interactions parts, the file
|
||||
can be parsed with @scheme[unpack-submission] from
|
||||
@schememodname[handin-server/utils].
|
||||
|
||||
you can create this @filepath{users.ss} file:
|
||||
To submit an assignment as a group, students use a concatenation of
|
||||
usernames separated by ``@tt{+}'' and any number of spaces (e.g.,
|
||||
``@tt{user1+user2}''). The same syntax (``@tt{user1+user2}'') is
|
||||
used for the directory for shared submissions, where the usernames
|
||||
are always sorted so that directory names are deterministic.
|
||||
Multiple submissions for a particular user in different groups will
|
||||
be rejected.
|
||||
|
||||
@schemeblock[
|
||||
((foo ((unix "wRzN1u5q2SqRD") "L.E. Foo" "?"))
|
||||
(bar ((unix "$1$dKlU0OkJ$t63TzKz") "Bar Z. Lie" "?")))]
|
||||
Inactive assignment directories are used by the the HTTPS status web
|
||||
server.}
|
||||
|
||||
which can be combined with this setting for 'extra-fields in your
|
||||
@filepath{config.ss}:
|
||||
|
||||
@schemeblock[
|
||||
...
|
||||
(extra-fields (("Full Name" #f #f)
|
||||
("TA" '("Alice" "Bob") "Your TA")))
|
||||
...]
|
||||
|
||||
and you can tell your students to use their department username
|
||||
and password, and use the @onscreen{Manage ...} dialog to properly set
|
||||
their TA name.
|
||||
|
||||
Finally, a password value can be a list that begins with a
|
||||
@scheme['plaintext] symbol, which will be used without encryption. This
|
||||
may be useful for manually resetting a forgotten passwords.
|
||||
}
|
||||
|
||||
@item{@filepath{log} (or any other name that the 'log-file configuration option
|
||||
specifies (if any), created if not present, appended otherwise)
|
||||
: records connections and actions, where each entry is of the
|
||||
form
|
||||
@verbatim[
|
||||
"(id time-str msg-str)
|
||||
[<id>|<time>] <msg>}"]
|
||||
|
||||
where @scheme[<id>] is an integer representing the connection (numbered
|
||||
consecutively from 1 when the server starts), ``@tt{-}'' for a message
|
||||
without a connection, and ``@tt{wN}'' for a message from the status
|
||||
servlet.}
|
||||
|
||||
@item{Active and inactive assignment directories (which you can put in a
|
||||
nested directory for convenience, or specify a different absolute
|
||||
directory), as specified by the configuration file using the
|
||||
`active-dirs' and `inactive-dirs'. A list of active assignment
|
||||
directories (the last path element in each specified path is used
|
||||
as a label) is sent to the client tool when a student clicks
|
||||
@onscreen{Handin}. The assignment labels are ordered in the student's menu
|
||||
using @scheme[string<?], and the first assignment is the default
|
||||
selection.
|
||||
|
||||
Within each assignment directory, the student id is used for a
|
||||
sub-directory name. Within each student sub-directory are
|
||||
directories for handin attempts and successes. If a directory
|
||||
@filepath{ATTEMPT} exists, it contains the most recent (unsuccessful or
|
||||
currently-in-submission) handin attempt. Directories @filepath{SUCCESS-n}
|
||||
(where n counts from 0) contain successful handins; the lowest
|
||||
numbered such directory represents the latest handin.
|
||||
|
||||
A cleanup process in the server copies successful submissions to
|
||||
the student directory, one level up from the corresponding
|
||||
@filepath{SUCCESS-n} directory. This is done only for files and
|
||||
directories that are newer in @filepath{SUCCESS-n} than in the submission
|
||||
root, other files and directories are left intact. If external
|
||||
tools add new content to the student directory (eg, a @filepath{grade}
|
||||
file, as described below) it will stay there. If the machine
|
||||
crashes or the server is stopped, the cleanup process might not
|
||||
finish. When the server is started, it automatically runs the
|
||||
cleanup process for each student directory.
|
||||
|
||||
Within a student directory, a @filepath{handin.scm} file (or some other
|
||||
name if the @scheme[default-file-name] option is set) contains the actual
|
||||
submission. A @scheme[checker] procedure can change this default file
|
||||
name, and it can create additional files in an @filepath{ATTEMPT} directory
|
||||
(to be copied by the cleanup process); see below for more details
|
||||
on @schememodname[handin-server/checker].
|
||||
|
||||
For submissions from a normal DrScheme frame, a submission file
|
||||
contains a copy of the student's definitions and interactions
|
||||
windows. The file is in a binary format (to support non-text
|
||||
code), and opening the file directly in DrScheme shows the
|
||||
definitions part. To get both the definitions and interactions
|
||||
parts, the file can be parsed with @scheme[unpack-submission] from
|
||||
@schememodname[handin-server/utils].
|
||||
|
||||
To submit an assignment as a group, students use a concatenation
|
||||
of usernames separated by ``@tt{+}'' and any number of spaces (e.g.,
|
||||
``@tt{user1+user2}''). The same syntax (``@tt{user1+user2}'') is used for the
|
||||
directory for shared submissions, where the usernames are always
|
||||
sorted so that directory names are deterministic. Multiple
|
||||
submissions for a particular user in different groups will be
|
||||
rejected.
|
||||
|
||||
Inactive assignment directories are used by the the HTTPS status
|
||||
web server.}
|
||||
|
||||
@item{@filepath{<active-assignment>/checker.ss} (optional) : a module that
|
||||
exports a @scheme[checker] function. This function receives two
|
||||
@comment{use defproc here?}
|
||||
arguments: a username list and a submission as a byte string.
|
||||
(See also @scheme[unpack-submission], etc. from @schememodname[handin-server/utils].) To
|
||||
@item{@filepath{<active-assignment>/checker.ss} (optional): a module
|
||||
that exports a @scheme[checker] function. This function receives
|
||||
two
|
||||
@; use defproc here?
|
||||
arguments: a username list and a submission as a byte string. (See
|
||||
also @scheme[unpack-submission], etc. from
|
||||
@schememodname[handin-server/utils].) To
|
||||
reject the submission, the @scheme[checker] function can raise an
|
||||
exception; the exception message will be relayed back to the
|
||||
student. The module is loaded when the current directory is the
|
||||
|
@ -568,7 +567,7 @@ bar:$1$dKlU0OkJ$t63TzKz:1205:1205:Bar Z. Lie:/home/bar:/bin/bash"]
|
|||
|
||||
The checker should return a string, such as @filepath{handin.scm}, to use
|
||||
in naming the submission file, or @scheme[#f] to indicate that he file
|
||||
should be deleted (eg, when the checker alrady created the
|
||||
should be deleted (e.g., when the checker alrady created the
|
||||
submission file(s) in a different place).
|
||||
|
||||
Alternatively, the module can bind @scheme[checker] to a list of three
|
||||
|
@ -665,6 +664,7 @@ to obtain a list of all assignments, or
|
|||
to start with a specific assignment (named ASSIGNMENT). The default
|
||||
PORT is 7980.
|
||||
|
||||
|
||||
@section{Checker Utilities}
|
||||
|
||||
The checker utilities are provided to make writing checker functions.
|
||||
|
@ -692,6 +692,7 @@ common submission situations.}}
|
|||
|
||||
The following sections describe each of these modules.
|
||||
|
||||
|
||||
@section{Sandbox}
|
||||
|
||||
@defmodule[handin-server/sandbox]
|
||||
|
@ -699,6 +700,7 @@ The following sections describe each of these modules.
|
|||
This is just a wrapper around the sandbox engine from MzLib. It configures it
|
||||
for use with the handin server.
|
||||
|
||||
|
||||
@section{Utils}
|
||||
|
||||
@defmodule[handin-server/utils]
|
||||
|
@ -877,6 +879,7 @@ Checks whether @scheme[name] is defined in the evaluator @scheme[eval], and
|
|||
#f)]. (Note that before the checker is used (after the pre-checker, if
|
||||
specified), the timer will be reset to the @scheme['session-timeout] value.)}
|
||||
|
||||
|
||||
@section{checker}
|
||||
|
||||
@defmodulelang[handin-server/checker]{
|
||||
|
@ -1189,7 +1192,7 @@ values from the submission code.
|
|||
information includes only execution coverage by submission code, excluding
|
||||
additional checker tests. You do not have to call this explicitly---it is
|
||||
called at the end of the process automatically when @scheme[:coverage?] is
|
||||
enabled. It is made available so you can call it earlier (eg, before
|
||||
enabled. It is made available so you can call it earlier (e.g., before
|
||||
testing) to show clients a coverage error first, or if you want to avoid an
|
||||
error. For example, you can do this:
|
||||
|
||||
|
@ -1206,6 +1209,7 @@ values from the submission code.
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@section[#:tag "multi-file"]{Multiple-File Submissions}
|
||||
|
||||
By default, the system is set up for submissions of single a single
|
||||
|
@ -1221,7 +1225,7 @@ submissions, do the following:
|
|||
suffix that should be used for the single concatenated output file.}
|
||||
|
||||
@item{You can also add a @scheme[:names-checker] keyword--the value can be a
|
||||
regexp that all submitted files must follow (eg, @scheme[".*[.]scm$"]), or a
|
||||
regexp that all submitted files must follow (e.g., @scheme[".*[.]scm$"]), or a
|
||||
list of expected file names. Alternatively, it can be a 1-argument
|
||||
procedure that will receive the (sorted) list of submitted files and
|
||||
can throw an error if some files are missing or some files are
|
||||
|
@ -1258,6 +1262,7 @@ submission utility---the resulting executable can be used outside of
|
|||
DrScheme (but PLT Scheme is still required, so it cannot be
|
||||
uninstalled).
|
||||
|
||||
|
||||
@section{Auto-Updater}
|
||||
|
||||
The handin-client has code that can be used for automatic updating of
|
||||
|
|
|
@ -48,16 +48,14 @@ re-exported by @schememodname[net/url].}
|
|||
The basic structure for all URLs, hich is explained in RFC 3986
|
||||
@cite["RFC3986"]. The following diagram illustrates the parts:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
http://sky@www:801/cgi-bin/finger;xyz?name=shriram;host=nw#top
|
||||
{-1} {2} {3} {4}{---5-------------} {----7-------------} {8}
|
||||
{6}
|
||||
{-1} {2} {3} {4}{---5---------} {6} {----7-------------} {8}
|
||||
|
||||
1 = scheme, 2 = user, 3 = host, 4 = port,
|
||||
5 = path (two elements), 6 = param (of second path element),
|
||||
7 = query, 8 = fragment
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
The strings inside the @scheme[user], @scheme[path], @scheme[query],
|
||||
and @scheme[fragment] fields are represented directly as Scheme
|
||||
|
|
|
@ -634,7 +634,7 @@ control can be placed into struct fields.
|
|||
|
||||
As an example, consider the following C code:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef struct { int x; char y; } A;
|
||||
typedef struct { A a; int z; } B;
|
||||
|
||||
|
@ -656,8 +656,7 @@ As an example, consider the following C code:
|
|||
char gety(A* a) {
|
||||
return a->y;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Using the simple @scheme[_list-struct], you might expect this code to
|
||||
work:
|
||||
|
|
|
@ -330,8 +330,7 @@ The built-in container classes include horizontal panels (and panes),
|
|||
vertical containers, a programmer can achieve most any layout. For
|
||||
example, we can construct a dialog with the following shape:
|
||||
|
||||
@verbatim[
|
||||
#<<IMG
|
||||
@verbatim{
|
||||
------------------------------------------------------
|
||||
| ------------------------------------- |
|
||||
| Your name: | | |
|
||||
|
@ -340,8 +339,7 @@ The built-in container classes include horizontal panels (and panes),
|
|||
| ( Cancel ) ( OK ) |
|
||||
| -------- ---- |
|
||||
------------------------------------------------------
|
||||
IMG
|
||||
]
|
||||
}
|
||||
|
||||
with the following program:
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ When @exec{mzscheme} is run with no command-line arguments (other than
|
|||
confguration options, like @Flag{j}), then it starts a @tech{REPL}
|
||||
with a @litchar{> } prompt:
|
||||
|
||||
@verbatim[" Welcome to MzScheme\n > "]
|
||||
@verbatim{ Welcome to MzScheme\n > }
|
||||
|
||||
@margin-note{For information on GNU Readline support, see
|
||||
@schememodname[readline/rep].}
|
||||
|
|
|
@ -18,12 +18,11 @@ executable followed by a module declaration. For example, if
|
|||
@exec{mzscheme} is installed in @filepath{/usr/local/bin}, then a file
|
||||
containing the following text acts as a ``hello world'' script:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
#! /usr/local/bin/mzscheme
|
||||
#lang scheme/base
|
||||
"Hello, world!"
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
In particular, if the above is put into a file @filepath{hello} and
|
||||
the file is made executable (e.g., with @exec{chmod a+x hello}), then
|
||||
|
@ -40,23 +39,21 @@ executable, a popular alternative is to require that @exec{mzscheme}
|
|||
is in the user's command path, and then ``trampoline'' using
|
||||
@exec{/usr/bin/env}:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
#! /usr/bin/env mzscheme
|
||||
#lang scheme/base
|
||||
"Hello, world!"
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
In either case, command-line arguments to a script are available via
|
||||
@scheme[current-command-line-arguments]:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
#! /usr/bin/env mzscheme
|
||||
#lang scheme/base
|
||||
(printf "Given arguments: ~s\n"
|
||||
(current-command-line-arguments))
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
If the name of the script is needed, it is available via
|
||||
@scheme[(find-system-path 'run-file)], instead of via
|
||||
|
@ -68,7 +65,7 @@ them using the @scheme[command-line] form provided by
|
|||
command-line arguments from @scheme[(current-command-line-arguments)]
|
||||
by default:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
#! /usr/bin/env mzscheme
|
||||
#lang scheme
|
||||
|
||||
|
@ -84,8 +81,7 @@ by default:
|
|||
(printf "~a~a\n"
|
||||
greeting
|
||||
(if (verbose?) " to you, too!" ""))
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Try running the above script with the @DFlag{help} flag to see what
|
||||
command-line arguments are allowed by the script.
|
||||
|
@ -95,7 +91,7 @@ that are comments in one language and expressions in the other. This
|
|||
trampoline is more complicated, but it provides more control over
|
||||
command-line arguments to @scheme{mzscheme}:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#! /bin/sh
|
||||
#|
|
||||
exec mzscheme -cu "$0" ${1+"$@"}
|
||||
|
@ -105,8 +101,7 @@ command-line arguments to @scheme{mzscheme}:
|
|||
(printf "bytecode files has been disabled via -c.\n")
|
||||
(printf "Given arguments: ~s\n"
|
||||
(current-command-line-arguments))
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
Note that @litchar{#!} starts a line comment in Scheme, and
|
||||
@litchar{#|}...@litchar{|#} forms a block comment. Meanwhile,
|
||||
|
|
|
@ -41,11 +41,10 @@ The @var{f} function is called by the custodian if it is ever asked to
|
|||
``shutdown'' its values; @var{o} and @var{data} are passed on to
|
||||
@var{f}, which has the type
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef void (*Scheme_Close_Custodian_Client)(Scheme_Object *o,
|
||||
void *data);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
If @var{strong} is non-zero, then the newly managed value will
|
||||
be remembered until either the custodian shuts it down or
|
||||
|
@ -85,13 +84,12 @@ Installs a function to be called on each custodian-registered item and
|
|||
its closer when MzScheme is about to exit. The registered function
|
||||
has the type
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef
|
||||
void (*Scheme_Exit_Closer_Func)(Scheme_Object *o,
|
||||
Scheme_Close_Custodian_Client *f,
|
||||
void *d);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
where @var{d} is the second argument for @var{f}.}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ For example, consider the following implementation of a
|
|||
performs @scheme[or] on the results of the thunks, evaluating only as
|
||||
many thunks as necessary.
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
static Scheme_Object *
|
||||
thunk_or (int argc, Scheme_Object **argv)
|
||||
{
|
||||
|
@ -74,8 +74,7 @@ thunk_or (int argc, Scheme_Object **argv)
|
|||
|
||||
return scheme_tail_apply(argv[argc - 1], 0, NULL);
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
This @scheme[thunk-or] properly implements tail-recursion: if the
|
||||
final thunk is applied, then the result of @scheme[thunk-or] is the
|
||||
|
|
|
@ -18,7 +18,7 @@ in @cpp{scheme_current_thread->error_buf}. The macro
|
|||
@cppi{scheme_error_buf} is a shorthand for
|
||||
@cpp{*scheme_current_thread->error_buf}.
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
mz_jmp_buf * volatile save, fresh;
|
||||
...
|
||||
save = scheme_current_thread->error_buf;
|
||||
|
@ -31,8 +31,7 @@ in @cpp{scheme_current_thread->error_buf}. The macro
|
|||
}
|
||||
scheme_current_thread->error_buf = save;
|
||||
...
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
3m: when @cpp{scheme_setjmp} is used, the enclosing context must
|
||||
provide a local-variable registration record via @cpp{MZ_GC_DECL_REG}.
|
||||
|
@ -85,7 +84,7 @@ visible when implementing a new primitive procedure. When
|
|||
request by chaining to the previously saved error buffer; otherwise,
|
||||
call @cppi{scheme_clear_escape}.
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
mz_jmp_buf * volatile save, fresh;
|
||||
save = scheme_current_thread->error_buf;
|
||||
scheme_current_thread->error_buf = &fresh;
|
||||
|
@ -102,8 +101,7 @@ call @cppi{scheme_clear_escape}.
|
|||
scheme_eval_string("x", scheme_env);
|
||||
}
|
||||
scheme_current_thread->error_buf = save;
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
This solution works fine as long as the procedure implementation only
|
||||
calls top-level evaluation functions (@cpp{scheme_eval},
|
||||
|
@ -122,97 +120,95 @@ exceptions in a variety of situations. It implements the function
|
|||
application of a thunk. (This code is in
|
||||
@filepath{collects/mzscheme/examples/catch.c} in the distribution.)
|
||||
|
||||
@verbatim[#<<EOS
|
||||
static Scheme_Object *exn_catching_apply, *exn_p, *exn_message;
|
||||
@verbatim{
|
||||
static Scheme_Object *exn_catching_apply, *exn_p, *exn_message;
|
||||
|
||||
static void init_exn_catching_apply()
|
||||
{
|
||||
if (!exn_catching_apply) {
|
||||
char *e =
|
||||
"(lambda (thunk) "
|
||||
"(with-handlers ([void (lambda (exn) (cons #f exn))]) "
|
||||
"(cons #t (thunk))))";
|
||||
/* make sure we have a namespace with the standard bindings: */
|
||||
Scheme_Env *env = (Scheme_Env *)scheme_make_namespace(0, NULL);
|
||||
static void init_exn_catching_apply()
|
||||
{
|
||||
if (!exn_catching_apply) {
|
||||
char *e =
|
||||
"(lambda (thunk) "
|
||||
"(with-handlers ([void (lambda (exn) (cons #f exn))]) "
|
||||
"(cons #t (thunk))))";
|
||||
/* make sure we have a namespace with the standard bindings: */
|
||||
Scheme_Env *env = (Scheme_Env *)scheme_make_namespace(0, NULL);
|
||||
|
||||
scheme_register_extension_global(&exn_catching_apply,
|
||||
sizeof(Scheme_Object *));
|
||||
scheme_register_extension_global(&exn_p,
|
||||
sizeof(Scheme_Object *));
|
||||
scheme_register_extension_global(&exn_message,
|
||||
sizeof(Scheme_Object *));
|
||||
|
||||
exn_catching_apply = scheme_eval_string(e, env);
|
||||
exn_p = scheme_lookup_global(scheme_intern_symbol("exn?"), env);
|
||||
exn_message
|
||||
= scheme_lookup_global(scheme_intern_symbol("exn-message"),
|
||||
env);
|
||||
scheme_register_extension_global(&exn_catching_apply,
|
||||
sizeof(Scheme_Object *));
|
||||
scheme_register_extension_global(&exn_p,
|
||||
sizeof(Scheme_Object *));
|
||||
scheme_register_extension_global(&exn_message,
|
||||
sizeof(Scheme_Object *));
|
||||
|
||||
exn_catching_apply = scheme_eval_string(e, env);
|
||||
exn_p = scheme_lookup_global(scheme_intern_symbol("exn?"), env);
|
||||
exn_message
|
||||
= scheme_lookup_global(scheme_intern_symbol("exn-message"),
|
||||
env);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function applies a thunk, returning the Scheme value if
|
||||
there's no exception, otherwise returning NULL and setting *exn
|
||||
to the raised value (usually an exn structure). */
|
||||
Scheme_Object *_apply_thunk_catch_exceptions(Scheme_Object *f,
|
||||
Scheme_Object **exn)
|
||||
{
|
||||
Scheme_Object *v;
|
||||
|
||||
init_exn_catching_apply();
|
||||
|
||||
v = _scheme_apply(exn_catching_apply, 1, &f);
|
||||
/* v is a pair: (cons #t value) or (cons #f exn) */
|
||||
|
||||
if (SCHEME_TRUEP(SCHEME_CAR(v)))
|
||||
return SCHEME_CDR(v);
|
||||
else {
|
||||
*exn = SCHEME_CDR(v);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Scheme_Object *extract_exn_message(Scheme_Object *v)
|
||||
{
|
||||
init_exn_catching_apply();
|
||||
|
||||
if (SCHEME_TRUEP(_scheme_apply(exn_p, 1, &v)))
|
||||
return _scheme_apply(exn_message, 1, &v);
|
||||
else
|
||||
return NULL; /* Not an exn structure */
|
||||
}
|
||||
}
|
||||
|
||||
/* This function applies a thunk, returning the Scheme value if
|
||||
there's no exception, otherwise returning NULL and setting *exn
|
||||
to the raised value (usually an exn structure). */
|
||||
Scheme_Object *_apply_thunk_catch_exceptions(Scheme_Object *f,
|
||||
Scheme_Object **exn)
|
||||
{
|
||||
Scheme_Object *v;
|
||||
|
||||
init_exn_catching_apply();
|
||||
|
||||
v = _scheme_apply(exn_catching_apply, 1, &f);
|
||||
/* v is a pair: (cons #t value) or (cons #f exn) */
|
||||
|
||||
if (SCHEME_TRUEP(SCHEME_CAR(v)))
|
||||
return SCHEME_CDR(v);
|
||||
else {
|
||||
*exn = SCHEME_CDR(v);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Scheme_Object *extract_exn_message(Scheme_Object *v)
|
||||
{
|
||||
init_exn_catching_apply();
|
||||
|
||||
if (SCHEME_TRUEP(_scheme_apply(exn_p, 1, &v)))
|
||||
return _scheme_apply(exn_message, 1, &v);
|
||||
else
|
||||
return NULL; /* Not an exn structure */
|
||||
}
|
||||
EOS
|
||||
]
|
||||
|
||||
In the following example, the above code is used to catch exceptions
|
||||
that occur during while evaluating source code from a string.
|
||||
|
||||
@verbatim[#<<EOS
|
||||
static Scheme_Object *do_eval(void *s, int noargc, Scheme_Object **noargv)
|
||||
{
|
||||
return scheme_eval_string((char *)s, scheme_get_env(scheme_config));
|
||||
@verbatim{
|
||||
static Scheme_Object *do_eval(void *s, int noargc, Scheme_Object **noargv)
|
||||
{
|
||||
return scheme_eval_string((char *)s, scheme_get_env(scheme_config));
|
||||
}
|
||||
|
||||
static Scheme_Object *eval_string_or_get_exn_message(char *s)
|
||||
{
|
||||
Scheme_Object *v, *exn;
|
||||
|
||||
v = scheme_make_closed_prim(do_eval, s);
|
||||
v = _apply_thunk_catch_exceptions(v, &exn);
|
||||
/* Got a value? */
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
v = extract_exn_message(exn);
|
||||
/* Got an exn? */
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
/* `raise' was called on some arbitrary value */
|
||||
return exn;
|
||||
}
|
||||
}
|
||||
|
||||
static Scheme_Object *eval_string_or_get_exn_message(char *s)
|
||||
{
|
||||
Scheme_Object *v, *exn;
|
||||
|
||||
v = scheme_make_closed_prim(do_eval, s);
|
||||
v = _apply_thunk_catch_exceptions(v, &exn);
|
||||
/* Got a value? */
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
v = extract_exn_message(exn);
|
||||
/* Got an exn? */
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
/* `raise' was called on some arbitrary value */
|
||||
return exn;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
|
||||
@section{Enabling and Disabling Breaks}
|
||||
|
|
|
@ -206,39 +206,36 @@ pointer within a function call at any point when a collection can be
|
|||
triggered. Beware that nested function calls can hide temporary
|
||||
pointers; for example, in
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
scheme_make_pair(scheme_make_pair(scheme_true, scheme_false),
|
||||
scheme_make_pair(scheme_false, scheme_true))
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
the result from one @cpp{scheme_make_pair} call is on the stack or in
|
||||
a register during the other call to @cpp{scheme_make_pair}; this
|
||||
pointer must be exposed to the garbage collection and made subject to
|
||||
update. Simply changing the code to
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
tmp = scheme_make_pair(scheme_true, scheme_false);
|
||||
scheme_make_pair(tmp,
|
||||
scheme_make_pair(scheme_false, scheme_true))
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
does not expose all pointers, since @cpp{tmp} must be evaluated before
|
||||
the second call to @cpp{scheme_make_pair}. In general, the above code
|
||||
must be converted to the form
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
tmp1 = scheme_make_pair(scheme_true, scheme_false);
|
||||
tmp2 = scheme_make_pair(scheme_true, scheme_false);
|
||||
scheme_make_pair(tmp1, tmp2);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
and this is converted form must be instrumented to register @cpp{tmp1}
|
||||
and @cpp{tmp2}. The final result might be
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
{
|
||||
Scheme_Object *tmp1 = NULL, *tmp2 = NULL, *result;
|
||||
MZ_GC_DECL_REG(2);
|
||||
|
@ -255,8 +252,7 @@ and @cpp{tmp2}. The final result might be
|
|||
|
||||
return result;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Notice that @cpp{result} is not registered above. The
|
||||
@cpp{MZ_GC_UNREG} macro cannot trigger a garbage collection, so the
|
||||
|
@ -273,7 +269,7 @@ whereas registering an array of pointers requires three slots. For
|
|||
example, to register a pointer @cpp{tmp} and an array of 10
|
||||
@cpp{char*}s:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
{
|
||||
Scheme_Object *tmp1 = NULL;
|
||||
char *a[10];
|
||||
|
@ -288,8 +284,7 @@ example, to register a pointer @cpp{tmp} and an array of 10
|
|||
f(a);
|
||||
...
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
The @cppi{MZ_GC_ARRAY_VAR_IN_REG} macro registers a local array given
|
||||
a starting slot, the array variable, and an array size. The
|
||||
|
@ -305,7 +300,7 @@ must be registered with the collector during the entire call to
|
|||
The name used for a variable need not be immediate. Structure members
|
||||
can be supplied as well:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
{
|
||||
struct { void *s; int v; void *t; } x = {NULL, 0, NULL};
|
||||
MZ_GC_DECL_REG(2);
|
||||
|
@ -314,8 +309,7 @@ can be supplied as well:
|
|||
MZ_GC_VAR_IN_REG(0, x.t);
|
||||
...
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
In general, the only constraint on the second argument to
|
||||
@cppi{MZ_GC_VAR_IN_REG} or @cppi{MZ_GC_ARRAY_VAR_IN_REG} is that
|
||||
|
@ -336,7 +330,7 @@ example also illustrates how @cpp{MZ_GC_UNREG} is not needed when
|
|||
control escapes from the function, such as when
|
||||
@cpp{scheme_signal_error} escapes.
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
{
|
||||
Scheme_Object *tmp1 = NULL, *tmp2 = NULL;
|
||||
mzchar *a, *b;
|
||||
|
@ -371,15 +365,14 @@ control escapes from the function, such as when
|
|||
|
||||
return tmp1;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
A @cpp{MZ_GC_DECL_REG} can be used in a nested block to hold
|
||||
declarations for the block's variables. In that case, the nested
|
||||
@cpp{MZ_GC_DECL_REG} must have its own @cpp{MZ_GC_REG} and
|
||||
@cpp{MZ_GC_UNREG} calls.
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
{
|
||||
Scheme_Object *accum = NULL;
|
||||
MZ_GC_DECL_REG(1);
|
||||
|
@ -403,8 +396,7 @@ declarations for the block's variables. In that case, the nested
|
|||
MZ_GC_UNREG();
|
||||
return accum;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Variables declared in a local block can also be registered together
|
||||
with variables from an enclosing block, but the local-block variable
|
||||
|
@ -412,7 +404,7 @@ must be unregistered before it goes out of scope. The
|
|||
@cppi{MZ_GC_NO_VAR_IN_REG} macro can be used to unregister a variable
|
||||
or to initialize a slot as having no variable.
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
{
|
||||
Scheme_Object *accum = NULL;
|
||||
MZ_GC_DECL_REG(2);
|
||||
|
@ -435,8 +427,7 @@ or to initialize a slot as having no variable.
|
|||
MZ_GC_UNREG();
|
||||
return accum;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
The @cpp{MZ_GC_} macros all expand to nothing when @cpp{MZ_PRECISE_GC}
|
||||
is not defined, so the macros can be placed into code to be compiled
|
||||
|
@ -536,23 +527,22 @@ The following macros can be used (with care!) to navigate
|
|||
|
||||
Example:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
int foo(int c, ...) {
|
||||
int r = 0;
|
||||
XFORM_START_SKIP;
|
||||
{
|
||||
/* va plays strange tricks that confuse xform */
|
||||
va_list args;
|
||||
va_start(args, c);
|
||||
while (c--) {
|
||||
r += va_arg(args, int);
|
||||
@verbatim{
|
||||
int foo(int c, ...) {
|
||||
int r = 0;
|
||||
XFORM_START_SKIP;
|
||||
{
|
||||
/* va plays strange tricks that confuse xform */
|
||||
va_list args;
|
||||
va_start(args, c);
|
||||
while (c--) {
|
||||
r += va_arg(args, int);
|
||||
}
|
||||
}
|
||||
XFORM_END_SKIP;
|
||||
return r;
|
||||
}
|
||||
XFORM_END_SKIP;
|
||||
return r;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
|
||||
These macros can also be used at the top level, outside of any
|
||||
function. Since they have to be terminated by a semi-colon, however,
|
||||
|
@ -565,21 +555,20 @@ EOS
|
|||
|
||||
Example:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
int foo(int c, ...) {
|
||||
int r = 0;
|
||||
{
|
||||
/* va plays strange tricks that confuse xform */
|
||||
XFORM_CAN_IGNORE va_list args; /* See below */
|
||||
XFORM_HIDE_EXPR(va_start(args, c));
|
||||
while (c--) {
|
||||
r += XFORM_HIDE_EXPR(va_arg(args, int));
|
||||
@verbatim{
|
||||
int foo(int c, ...) {
|
||||
int r = 0;
|
||||
{
|
||||
/* va plays strange tricks that confuse xform */
|
||||
XFORM_CAN_IGNORE va_list args; /* See below */
|
||||
XFORM_HIDE_EXPR(va_start(args, c));
|
||||
while (c--) {
|
||||
r += XFORM_HIDE_EXPR(va_arg(args, int));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
EOS
|
||||
]}
|
||||
}}
|
||||
|
||||
@item{@cppi{XFORM_CAN_IGNORE}: a macro that acts like a type
|
||||
modifier (must appear first) to indicate that a declared variable
|
||||
|
@ -710,14 +699,13 @@ Overrides the GC's auto-determined stack base, and/or disables the
|
|||
|
||||
The following example shows a typical use for setting the stack base:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
int main(int argc, char **argv) {
|
||||
int dummy;
|
||||
scheme_set_stack_base(&dummy, 0);
|
||||
real_main(argc, argv); /* calls scheme_basic_env(), etc. */
|
||||
}
|
||||
EOS
|
||||
]}
|
||||
}}
|
||||
|
||||
@function[(void scheme_set_stack_bounds
|
||||
[void* stack_addr]
|
||||
|
@ -789,7 +777,7 @@ finalizers are registered for @var{p}.
|
|||
The @cpp{fnl_proc} type is not actually defined, but it is equivalent
|
||||
to
|
||||
|
||||
@verbatim[" typedef void (*fnl_proc)(void *p, void *data)"]
|
||||
@verbatim{ typedef void (*fnl_proc)(void *p, void *data)}
|
||||
|
||||
The @var{f} argument is the callback function; when it is called, it
|
||||
will be passed the value @var{p} and the data pointer @var{data};
|
||||
|
@ -908,12 +896,11 @@ Forces an immediate garbage-collection.}
|
|||
|
||||
Each of the three procedures takes a pointer and returns an integer:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef int (*Size_Proc)(void *obj);
|
||||
typedef int (*Mark_Proc)(void *obj);
|
||||
typedef int (*Fixup_Proc)(void *obj);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
If the result of the size procedure is a constant, then pass a
|
||||
non-zero value for @var{is_const_size}. If the mark and fixup
|
||||
|
|
|
@ -168,7 +168,7 @@ tables) can be anything.
|
|||
The public portion of the @cppi{Scheme_Hash_Table} type is defined
|
||||
roughly as follows:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef struct Scheme_Hash_Table {
|
||||
Scheme_Object so; /* so.type == scheme_hash_table_type */
|
||||
/* ... */
|
||||
|
@ -180,8 +180,7 @@ roughly as follows:
|
|||
int (*compare)(void *v1, void *v2);
|
||||
/* ... */
|
||||
} Scheme_Hash_Table;
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
The @cpp{make_hash_indices} and @cpp{compare} function pointers can be
|
||||
set to arbitrary hashing and comparison functions (before any mapping
|
||||
|
@ -235,7 +234,7 @@ two are the same as for hash tables. The last is like
|
|||
The public portion of the @cppi{Scheme_Bucket_Table} type is defined
|
||||
roughly as follows:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef struct Scheme_Bucket_Table {
|
||||
Scheme_Object so; /* so.type == scheme_variable_type */
|
||||
/* ... */
|
||||
|
@ -246,8 +245,7 @@ roughly as follows:
|
|||
int (*compare)(void *v1, void *v2);
|
||||
/* ... */
|
||||
} Scheme_Bucket_Table;
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
The @cpp{make_hash_indices} and @cpp{compare} functions are used as
|
||||
for hash tables. Note that @cppi{SCHEME_hash_weak_ptr} supplied as the
|
||||
|
@ -288,15 +286,14 @@ Returns the current value for @var{key} in @var{table}, or @cpp{NULL}
|
|||
Returns the bucket for @var{key} in @var{table}. The
|
||||
@cppi{Scheme_Bucket} structure is defined as:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef struct Scheme_Bucket {
|
||||
Scheme_Object so; /* so.type == scheme_bucket_type */
|
||||
/* ... */
|
||||
void *key;
|
||||
void *val;
|
||||
} Scheme_Bucket;
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Setting @var{val} to @cpp{NULL} unmaps the bucket's key, and @var{key}
|
||||
can be @cpp{NULL} in that case as well. If the table holds keys
|
||||
|
|
|
@ -78,14 +78,13 @@ the global variable is undefined.
|
|||
|
||||
The @cppi{Scheme_Bucket} structure is defined as:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef struct Scheme_Bucket {
|
||||
Scheme_Object so; /* so.type = scheme_variable_type */
|
||||
void *key;
|
||||
void *val;
|
||||
} Scheme_Bucket;
|
||||
EOS
|
||||
]}
|
||||
}}
|
||||
|
||||
@function[(Scheme_Bucket* scheme_module_bucket
|
||||
[Scheme_Object* mod]
|
||||
|
|
|
@ -160,7 +160,7 @@ registered using @cppi{scheme_register_extension_global} (see
|
|||
As an example, the following C code defines an extension that returns
|
||||
@scheme["hello world"] when it is loaded:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
#include "escheme.h"
|
||||
Scheme_Object *scheme_initialize(Scheme_Env *env) {
|
||||
return scheme_make_utf8_string("hello world");
|
||||
|
@ -171,8 +171,7 @@ As an example, the following C code defines an extension that returns
|
|||
Scheme_Object *scheme_module_name() {
|
||||
return scheme_false;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Assuming that this code is in the file @filepath{hw.c}, the extension
|
||||
is compiled under Unix with the following two commands:
|
||||
|
@ -323,7 +322,7 @@ evaluates all expressions provided on the command line and displays
|
|||
the results, then runs a @scheme[read]-@scheme[eval]-@scheme[print]
|
||||
loop:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
#include "scheme.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -358,8 +357,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Under Mac OS X, or under Windows when MzScheme is compiled to a DLL
|
||||
using Cygwin, the garbage collector cannot find static variables
|
||||
|
@ -432,7 +430,7 @@ The simple embedding program from the previous section can be
|
|||
extended to work with either CGC or 3m, dependong on whether
|
||||
@cpp{MZ_PRECISE_GC} is specified on the compiler's command line:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
#include "scheme.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -487,8 +485,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
return 0;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Strictly speaking, the @cpp{config} and @cpp{v} variables above need not be
|
||||
registered with the garbage collector, since their values are not needed
|
||||
|
|
|
@ -47,11 +47,10 @@ substantial or unbounded work should occasionally call
|
|||
Creates a primitive procedure value, given the C function pointer
|
||||
@var{prim}. The form of @var{prim} is defined by:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef Scheme_Object *(Scheme_Prim)(int argc,
|
||||
Scheme_Object **argv);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
The value @var{mina} should be the minimum number of arguments that
|
||||
must be supplied to the procedure. The value @var{maxa} should be the
|
||||
|
@ -95,13 +94,12 @@ in @var{vals}; when the C function @var{prim} is invoked, the
|
|||
generated primitive is passed as the last parameter. The form of
|
||||
@var{prim} is defined by:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef
|
||||
Scheme_Object *(Scheme_Prim_Closure_Proc)(int argc,
|
||||
Scheme_Object **argv,
|
||||
Scheme_Object *prim);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
The macro @cppi{SCHEME_PRIM_CLOSURE_ELS} takes a primitive-closure
|
||||
object and returns an array with the same length and content as
|
||||
|
@ -119,12 +117,11 @@ Creates an old-style primitive procedure value; when the C function
|
|||
@var{prim} is invoked, @var{data} is passed as the first parameter.
|
||||
The form of @var{prim} is defined by:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef
|
||||
Scheme_Object *(Scheme_Closed_Prim)(void *data, int argc,
|
||||
Scheme_Object **argv);
|
||||
EOS
|
||||
]}
|
||||
}}
|
||||
|
||||
@function[(Scheme_Object* scheme_make_closed_prim
|
||||
[Scheme_Closed_Prim* prim]
|
||||
|
|
|
@ -85,23 +85,22 @@ of ``fuel'' that has been consumed since the last call to
|
|||
@scheme[vector->list] consumes a unit of fuel for each created cons
|
||||
cell:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
Scheme_Object *scheme_vector_to_list(Scheme_Object *vec)
|
||||
{
|
||||
int i;
|
||||
Scheme_Object *pair = scheme_null;
|
||||
@verbatim{
|
||||
Scheme_Object *scheme_vector_to_list(Scheme_Object *vec)
|
||||
{
|
||||
int i;
|
||||
Scheme_Object *pair = scheme_null;
|
||||
|
||||
i = SCHEME_VEC_SIZE(vec);
|
||||
i = SCHEME_VEC_SIZE(vec);
|
||||
|
||||
for (; i--; ) {
|
||||
SCHEME_USE_FUEL(1);
|
||||
pair = scheme_make_pair(SCHEME_VEC_ELS(vec)[i], pair);
|
||||
for (; i--; ) {
|
||||
SCHEME_USE_FUEL(1);
|
||||
pair = scheme_make_pair(SCHEME_VEC_ELS(vec)[i], pair);
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
|
||||
The @cpp{SCHEME_USE_FUEL} macro expands to a C block, not an
|
||||
expression.
|
||||
|
@ -174,42 +173,41 @@ class. (Any regular event-loop-based callback is appropriate.) The
|
|||
@cpp{scheme_notify_multithread} pointer is set to
|
||||
@cpp{MrEdInstallThreadTimer}. (MrEd no longer work this way, however.)
|
||||
|
||||
@verbatim[#<<EOS
|
||||
class MrEdThreadTimer : public wxTimer
|
||||
{
|
||||
public:
|
||||
void Notify(void); /* callback when timer expires */
|
||||
};
|
||||
@verbatim{
|
||||
class MrEdThreadTimer : public wxTimer
|
||||
{
|
||||
public:
|
||||
void Notify(void); /* callback when timer expires */
|
||||
};
|
||||
|
||||
static int threads_go;
|
||||
static MrEdThreadTimer *theThreadTimer;
|
||||
#define THREAD_WAIT_TIME 40
|
||||
static int threads_go;
|
||||
static MrEdThreadTimer *theThreadTimer;
|
||||
#define THREAD_WAIT_TIME 40
|
||||
|
||||
void MrEdThreadTimer::Notify()
|
||||
{
|
||||
if (threads_go)
|
||||
Start(THREAD_WAIT_TIME, TRUE);
|
||||
void MrEdThreadTimer::Notify()
|
||||
{
|
||||
if (threads_go)
|
||||
Start(THREAD_WAIT_TIME, TRUE);
|
||||
|
||||
scheme_check_threads();
|
||||
scheme_check_threads();
|
||||
}
|
||||
|
||||
static void MrEdInstallThreadTimer(int on)
|
||||
{
|
||||
if (!theThreadTimer)
|
||||
theThreadTimer = new MrEdThreadTimer;
|
||||
|
||||
if (on)
|
||||
theThreadTimer->Start(THREAD_WAIT_TIME, TRUE);
|
||||
else
|
||||
theThreadTimer->Stop();
|
||||
|
||||
threads_go = on;
|
||||
if (on)
|
||||
do_this_time = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void MrEdInstallThreadTimer(int on)
|
||||
{
|
||||
if (!theThreadTimer)
|
||||
theThreadTimer = new MrEdThreadTimer;
|
||||
|
||||
if (on)
|
||||
theThreadTimer->Start(THREAD_WAIT_TIME, TRUE);
|
||||
else
|
||||
theThreadTimer->Stop();
|
||||
|
||||
threads_go = on;
|
||||
if (on)
|
||||
do_this_time = 1;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
|
||||
An alternate architecture, which MrEd now uses, is to send the main
|
||||
thread into a loop, which blocks until an event is ready to handle.
|
||||
Scheme automatically takes care of running all threads, and it does so
|
||||
|
@ -242,84 +240,83 @@ ready on any of those file descriptors, the callbacks are removed and
|
|||
For example, the X Windows version of MrEd formerly set
|
||||
@cpp{scheme_wakeup_on_input} to this @cpp{MrEdNeedWakeup}:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
static XtInputId *scheme_cb_ids = NULL;
|
||||
static int num_cbs;
|
||||
@verbatim{
|
||||
static XtInputId *scheme_cb_ids = NULL;
|
||||
static int num_cbs;
|
||||
|
||||
static void MrEdNeedWakeup(void *fds)
|
||||
{
|
||||
int limit, count, i, p;
|
||||
fd_set *rd, *wr, *ex;
|
||||
static void MrEdNeedWakeup(void *fds)
|
||||
{
|
||||
int limit, count, i, p;
|
||||
fd_set *rd, *wr, *ex;
|
||||
|
||||
rd = (fd_set *)fds;
|
||||
wr = ((fd_set *)fds) + 1;
|
||||
ex = ((fd_set *)fds) + 2;
|
||||
rd = (fd_set *)fds;
|
||||
wr = ((fd_set *)fds) + 1;
|
||||
ex = ((fd_set *)fds) + 2;
|
||||
|
||||
limit = getdtablesize();
|
||||
limit = getdtablesize();
|
||||
|
||||
/* See if we need to do any work, really: */
|
||||
count = 0;
|
||||
for (i = 0; i < limit; i++) {
|
||||
if (MZ_FD_ISSET(i, rd))
|
||||
count++;
|
||||
if (MZ_FD_ISSET(i, wr))
|
||||
count++;
|
||||
if (MZ_FD_ISSET(i, ex))
|
||||
count++;
|
||||
/* See if we need to do any work, really: */
|
||||
count = 0;
|
||||
for (i = 0; i < limit; i++) {
|
||||
if (MZ_FD_ISSET(i, rd))
|
||||
count++;
|
||||
if (MZ_FD_ISSET(i, wr))
|
||||
count++;
|
||||
if (MZ_FD_ISSET(i, ex))
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
/* Remove old callbacks: */
|
||||
if (scheme_cb_ids)
|
||||
for (i = 0; i < num_cbs; i++)
|
||||
notify_set_input_func((Notify_client)NULL, (Notify_func)NULL,
|
||||
scheme_cb_ids[i]);
|
||||
|
||||
num_cbs = count;
|
||||
scheme_cb_ids = new int[num_cbs];
|
||||
|
||||
/* Install callbacks */
|
||||
p = 0;
|
||||
for (i = 0; i < limit; i++) {
|
||||
if (MZ_FD_ISSET(i, rd))
|
||||
scheme_cb_ids[p++] = XtAppAddInput(wxAPP_CONTEXT, i,
|
||||
(XtPointer *)XtInputReadMask,
|
||||
(XtInputCallbackProc)MrEdWakeUp, NULL);
|
||||
if (MZ_FD_ISSET(i, wr))
|
||||
scheme_cb_ids[p++] = XtAppAddInput(wxAPP_CONTEXT, i,
|
||||
(XtPointer *)XtInputWriteMask,
|
||||
(XtInputCallbackProc)MrEdWakeUp, NULL);
|
||||
if (MZ_FD_ISSET(i, ex))
|
||||
scheme_cb_ids[p++] = XtAppAddInput(wxAPP_CONTEXT, i,
|
||||
(XtPointer *)XtInputExceptMask,
|
||||
(XtInputCallbackProc)MrEdWakeUp,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
/* callback function when input/exception is detected: */
|
||||
Bool MrEdWakeUp(XtPointer, int *, XtInputId *)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Remove old callbacks: */
|
||||
if (scheme_cb_ids)
|
||||
for (i = 0; i < num_cbs; i++)
|
||||
notify_set_input_func((Notify_client)NULL, (Notify_func)NULL,
|
||||
scheme_cb_ids[i]);
|
||||
if (scheme_cb_ids) {
|
||||
/* Remove all callbacks: */
|
||||
for (i = 0; i < num_cbs; i++)
|
||||
XtRemoveInput(scheme_cb_ids[i]);
|
||||
|
||||
num_cbs = count;
|
||||
scheme_cb_ids = new int[num_cbs];
|
||||
scheme_cb_ids = NULL;
|
||||
|
||||
/* Install callbacks */
|
||||
p = 0;
|
||||
for (i = 0; i < limit; i++) {
|
||||
if (MZ_FD_ISSET(i, rd))
|
||||
scheme_cb_ids[p++] = XtAppAddInput(wxAPP_CONTEXT, i,
|
||||
(XtPointer *)XtInputReadMask,
|
||||
(XtInputCallbackProc)MrEdWakeUp, NULL);
|
||||
if (MZ_FD_ISSET(i, wr))
|
||||
scheme_cb_ids[p++] = XtAppAddInput(wxAPP_CONTEXT, i,
|
||||
(XtPointer *)XtInputWriteMask,
|
||||
(XtInputCallbackProc)MrEdWakeUp, NULL);
|
||||
if (MZ_FD_ISSET(i, ex))
|
||||
scheme_cb_ids[p++] = XtAppAddInput(wxAPP_CONTEXT, i,
|
||||
(XtPointer *)XtInputExceptMask,
|
||||
(XtInputCallbackProc)MrEdWakeUp,
|
||||
NULL);
|
||||
/* ``wake up'' */
|
||||
scheme_wake_up();
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* callback function when input/exception is detected: */
|
||||
Bool MrEdWakeUp(XtPointer, int *, XtInputId *)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (scheme_cb_ids) {
|
||||
/* Remove all callbacks: */
|
||||
for (i = 0; i < num_cbs; i++)
|
||||
XtRemoveInput(scheme_cb_ids[i]);
|
||||
|
||||
scheme_cb_ids = NULL;
|
||||
|
||||
/* ``wake up'' */
|
||||
scheme_wake_up();
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
EOS
|
||||
]
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
|
||||
@section[#:tag "sleeping"]{Sleeping by Embedded Scheme}
|
||||
|
@ -353,30 +350,29 @@ The following function @cpp{mzsleep} is an appropriate
|
|||
@cpp{scheme_sleep} function for most any Unix or Windows application.
|
||||
(This is approximately the built-in sleep used by Scheme.)
|
||||
|
||||
@verbatim[#<<EOS
|
||||
void mzsleep(float v, void *fds)
|
||||
{
|
||||
if (v) {
|
||||
sleep(v);
|
||||
} else {
|
||||
int limit;
|
||||
fd_set *rd, *wr, *ex;
|
||||
|
||||
# ifdef WIN32
|
||||
limit = 0;
|
||||
# else
|
||||
limit = getdtablesize();
|
||||
# endif
|
||||
@verbatim{
|
||||
void mzsleep(float v, void *fds)
|
||||
{
|
||||
if (v) {
|
||||
sleep(v);
|
||||
} else {
|
||||
int limit;
|
||||
fd_set *rd, *wr, *ex;
|
||||
|
||||
rd = (fd_set *)fds;
|
||||
wr = (fd_set *)scheme_get_fdset(fds, 1);
|
||||
ex = (fd_set *)scheme_get_fdset(fds, 2);
|
||||
# ifdef WIN32
|
||||
limit = 0;
|
||||
# else
|
||||
limit = getdtablesize();
|
||||
# endif
|
||||
|
||||
select(limit, rd, wr, ex, NULL);
|
||||
rd = (fd_set *)fds;
|
||||
wr = (fd_set *)scheme_get_fdset(fds, 1);
|
||||
ex = (fd_set *)scheme_get_fdset(fds, 2);
|
||||
|
||||
select(limit, rd, wr, ex, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
EOS
|
||||
]
|
||||
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
|
@ -476,12 +472,11 @@ Returns @cpp{1} if a break from @scheme[break-thread] or @cpp{scheme_break_threa
|
|||
The @cpp{Scheme_Ready_Fun} and @cpp{Scheme_Needs_Wakeup_Fun}
|
||||
types are defined as follows:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef int (*Scheme_Ready_Fun)(Scheme_Object *data);
|
||||
typedef void (*Scheme_Needs_Wakeup_Fun)(Scheme_Object *data,
|
||||
typedef void (*Scheme_Needs_Wakeup_Fun)(Scheme_Object *data,
|
||||
void *fds);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Blocks the current thread until @var{f} with @var{data} returns a true
|
||||
value. The @var{f} function is called periodically---at least once
|
||||
|
@ -564,7 +559,7 @@ for more information.}
|
|||
|
||||
@function[(void scheme_wake_up)]{
|
||||
|
||||
This function is called by the embedding program
|
||||
This function is called by the embedding program
|
||||
when there is input on an external file descriptor. See
|
||||
@secref["sleeping"] for more information.}
|
||||
|
||||
|
@ -628,13 +623,12 @@ Under Unix, and Mac OS X, this function has no effect.}
|
|||
|
||||
The argument types are defined as follows:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef int (*Scheme_Ready_Fun)(Scheme_Object *data);
|
||||
typedef void (*Scheme_Needs_Wakeup_Fun)(Scheme_Object *data,
|
||||
typedef void (*Scheme_Needs_Wakeup_Fun)(Scheme_Object *data,
|
||||
void *fds);
|
||||
typedef int (*Scheme_Wait_Filter_Fun)(Scheme_Object *data);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
Extends the set of waitable objects for @scheme[sync]
|
||||
to those with the type tag @var{type}. If @var{filter} is
|
||||
|
@ -655,12 +649,11 @@ Like @cpp{scheme_add_evt}, but for objects where waiting is based
|
|||
on a semaphore. Instead of @var{ready} and @var{wakeup} functions,
|
||||
the @var{getsema} function extracts a semaphore for a given object:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef
|
||||
Scheme_Object *(*Scheme_Wait_Sema_Fun)(Scheme_Object *data,
|
||||
Scheme_Object *(*Scheme_Wait_Sema_Fun)(Scheme_Object *data,
|
||||
int *repost);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
If a successful wait should leave the semaphore waited, then
|
||||
@var{getsema} should set @var{*repost} to @cpp{0}. Otherwise, the
|
||||
|
|
|
@ -683,13 +683,12 @@ Installs a printer to be used for printing (or writing or displaying)
|
|||
values that have the type tag @var{type}.
|
||||
|
||||
The type of @var{printer} is defined as follows:\cppIndex{scheme_Type_Printer}
|
||||
%
|
||||
@verbatim[#<<EOS
|
||||
|
||||
@verbatim{
|
||||
typedef void (*Scheme_Type_Printer)(Scheme_Object *v, int dis,
|
||||
Scheme_Print_Params *pp);
|
||||
EOS
|
||||
]
|
||||
%
|
||||
}
|
||||
|
||||
Such a printer must print a representation of the value using
|
||||
@cppi{scheme_print_bytes} and @cppi{scheme_print_string}. The
|
||||
first argument to the printer, @var{v}, is the value to be printed.
|
||||
|
@ -733,7 +732,7 @@ is only applied to values that both have tag @var{type}.
|
|||
The type of @var{equalp}, @var{hash1}, and @var{hash2} are defined as
|
||||
follows:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
typedef int (*Scheme_Equal_Proc)(Scheme_Object* obj1,
|
||||
Scheme_Object* obj2,
|
||||
void* cycle_data);
|
||||
|
@ -742,8 +741,7 @@ follows:
|
|||
void* cycle_data);
|
||||
typedef long (*Scheme_Secondary_Hash_Proc)(Scheme_Object* obj,
|
||||
void* cycle_data);
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
The two hash functions are use to generate primary and secondary keys
|
||||
for double hashing in an @scheme[equal?]-based hash table. The result
|
||||
|
|
|
@ -69,12 +69,11 @@ DrScheme.}
|
|||
@link[url:download-drscheme]{Download PLT Scheme}, install, and then
|
||||
start @exec{mzscheme} with no command-line arguments:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
$ mzscheme
|
||||
Welcome to MzScheme
|
||||
>
|
||||
EOS
|
||||
]
|
||||
}
|
||||
|
||||
If you're using a plain terminal, if you have GNU Readline installed
|
||||
on your system, and if you'd like Readline support in @exec{mzscheme},
|
||||
|
|
|
@ -35,11 +35,9 @@ called from Scheme as
|
|||
|
||||
or with an @elem["@"] expression as
|
||||
|
||||
@verbatim[
|
||||
#<<EOS
|
||||
@verbatim|{
|
||||
@title[#:tag "how-to"]{How to Design @italic{Great} Programs}
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
Although the procedures are mostly design to be used from @elem["@"]
|
||||
mode, they are easier to document in Scheme mode (partly because we
|
||||
|
|
|
@ -36,7 +36,7 @@ Some functions @deftech{decode} a sequence of @scheme[_pre-flow] or
|
|||
function accepts any number of @scheme[_pre-content] arguments, so
|
||||
that in
|
||||
|
||||
@verbatim[" @bold{``apple''}"]
|
||||
@verbatim|{ @bold{``apple''}}|
|
||||
|
||||
the @litchar{``apple''} argument is decoded to use fancy quotes, and
|
||||
then it is bolded.
|
||||
|
|
|
@ -25,15 +25,14 @@ To document a collection or @|PLaneT| package:
|
|||
@filepath{manual.scrbl}.}
|
||||
|
||||
@item{Start @filepath{manual.scrbl} like this:
|
||||
@verbatim[#<<EOS
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual)
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual)
|
||||
|
||||
@title{My Library}
|
||||
@title{My Library}
|
||||
|
||||
Welcome to my documentation: @scheme[(list 'testing 1 2 3)].
|
||||
EOS
|
||||
]
|
||||
Welcome to my documentation: @scheme[(list 'testing 1 2 3)].
|
||||
}|
|
||||
|
||||
The first line starts the file in ``text'' mode, and
|
||||
introduces the @litchar["@"] syntax to use Scheme bindings.
|
||||
|
@ -136,12 +135,11 @@ that precede text to typeset.
|
|||
|
||||
Thus,
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
@title{My Library}
|
||||
@scheme[(list 'testing 1 2 3)]
|
||||
@section[#:tag "here"]{You Are Here}
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
means
|
||||
|
||||
|
@ -191,7 +189,7 @@ preferred mechanism for linking to information outside of a single
|
|||
document. Such links require no information about where and how a
|
||||
binding is documented elsewhere:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual
|
||||
(for-label scheme))
|
||||
|
@ -199,15 +197,14 @@ binding is documented elsewhere:
|
|||
@title{My Library}
|
||||
|
||||
See also @scheme[list].
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
The @scheme[scheme] form typesets a Scheme expression for inline text,
|
||||
so it ignores the source formatting of the expression. The
|
||||
@scheme[schemeblock] form, in contrast, typesets inset Scheme code,
|
||||
and it preserves the expression's formatting from the document source.
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual
|
||||
(for-label scheme))
|
||||
|
@ -223,8 +220,7 @@ and it preserves the expression's formatting from the document source.
|
|||
"I've tried so hard to explain!"))
|
||||
(nobody-understands-me "glorble snop")
|
||||
]
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
|
||||
@; ----------------------------------------
|
||||
|
@ -238,7 +234,7 @@ hyperlink with text other than the section title.
|
|||
|
||||
The following example illustrates section hyperlinks:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual
|
||||
(for-label scheme))
|
||||
|
@ -252,15 +248,14 @@ The following example illustrates section hyperlinks:
|
|||
|
||||
|
||||
@section[#:tag "chickens"]{Philadelphia Chickens}
|
||||
|
||||
|
||||
Dancing tonight!
|
||||
|
||||
|
||||
@section{Reprise}
|
||||
|
||||
See @secref{chickens}.
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
Since the page is so short, it the hyperlinks in the above example are
|
||||
more effective if you change the @filepath{info.ss} file to add the
|
||||
|
@ -278,7 +273,7 @@ prefix, which is based on the target document's main source file. The
|
|||
following example links to a section in the PLT Scheme reference
|
||||
manual:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual
|
||||
(for-label scheme))
|
||||
|
@ -288,8 +283,7 @@ manual:
|
|||
@title{My Library}
|
||||
|
||||
See also @italic{@secref[#:doc ref-src]{pairs}}.
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
As mentioned in @secref{scheme-hyperlinks}, however, cross-document
|
||||
references based on @scheme[(require (for-label ....))] and
|
||||
|
@ -313,7 +307,7 @@ to import the binding information of @filepath{helper.ss}. Then add a
|
|||
binding with the module path as seen by a reader. Finally, use
|
||||
@scheme[defproc] to document the procedure:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual
|
||||
(for-label scheme
|
||||
|
@ -329,8 +323,7 @@ binding with the module path as seen by a reader. Finally, use
|
|||
|
||||
Replaces each @scheme['cow] in @scheme[lst] with
|
||||
@scheme['aardvark].}
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
In @scheme[defproc], a contract is specified with each argument to the
|
||||
procedure. In this example, the contract for the @scheme[_lst]
|
||||
|
@ -386,13 +379,13 @@ bindings introduced into the document source by
|
|||
from the previous section, then @filepath{helper.ss} must be imported both
|
||||
via @scheme[require-for-label] and @scheme[require]:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual
|
||||
scribble/eval ; <--- added
|
||||
"helper.ss" ; <--- added
|
||||
(for-label scheme
|
||||
"helper.ss"))]
|
||||
"helper.ss"))
|
||||
|
||||
@title{My Library}
|
||||
|
||||
|
@ -409,8 +402,7 @@ via @scheme[require-for-label] and @scheme[require]:
|
|||
(my-helper '())
|
||||
(my-helper '(cows such remarkable cows))
|
||||
]}
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
@;----------------------------------------
|
||||
@section{Splitting the Document Source}
|
||||
|
@ -423,7 +415,7 @@ as a sub-part of the enclosing part.
|
|||
|
||||
In @filepath{manual.scrbl}:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual)
|
||||
|
||||
|
@ -434,24 +426,22 @@ In @filepath{manual.scrbl}:
|
|||
|
||||
@include-section["cows.scrbl"]
|
||||
@include-section["aardvarks.scrbl"]
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
In @filepath{cows.scrbl}:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual)
|
||||
|
||||
@title{Cows}
|
||||
|
||||
Wherever they go, it's a quite a show.
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
In @filepath{aardvarks.scrbl}:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual
|
||||
(for-label scheme
|
||||
|
@ -464,8 +454,7 @@ In @filepath{aardvarks.scrbl}:
|
|||
|
||||
Replaces each @scheme['cow] in @scheme[lst] with
|
||||
@scheme['aardvark].}
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
@;----------------------------------------
|
||||
@section{Multi-Page Sections}
|
||||
|
@ -482,7 +471,7 @@ sub-sections.
|
|||
|
||||
Revising @filepath{cows.scrbl} from the previous section:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual)
|
||||
|
||||
|
@ -495,8 +484,7 @@ Revising @filepath{cows.scrbl} from the previous section:
|
|||
|
||||
@section{Dancing}
|
||||
See @secref["singing"].
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
To run this example, remember to change @filepath{info.ss} to add the
|
||||
@scheme['multi-page] style. You may also want to add a call to
|
||||
|
|
|
@ -27,7 +27,7 @@ alone, but @litchar["@"] forms can escape to S-expression mode.
|
|||
|
||||
A module written as
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim|{
|
||||
#lang scribble/doc
|
||||
@(require scribble/manual)
|
||||
|
||||
|
@ -37,8 +37,7 @@ A module written as
|
|||
|
||||
@bold{That} is the question.
|
||||
Whether 'tis nobler...
|
||||
EOS
|
||||
]
|
||||
}|
|
||||
|
||||
reads as
|
||||
|
||||
|
|
|
@ -201,11 +201,11 @@ following spaces (or tabs) are part of the comment (similar to
|
|||
Tip: if you're editing in a Scheme-aware editor (like DrScheme or
|
||||
Emacs), it is useful to comment out blocks like this:
|
||||
|
||||
@verbatim["
|
||||
@verbatim|{
|
||||
@;{
|
||||
...
|
||||
;}
|
||||
"]
|
||||
}|
|
||||
|
||||
so the editor does not treat the file as having unbalanced
|
||||
parenthesis.
|
||||
|
|
|
@ -57,14 +57,14 @@ when using @scheme[scheme], especially outside of @scheme[defproc] or
|
|||
@scheme[defform]. Prefix a meta-variable with @litchar{_}; for
|
||||
example,
|
||||
|
||||
@verbatim[" @scheme[(rator-expr rand-expr ...)]"]
|
||||
@verbatim|{ @scheme[(rator-expr rand-expr ...)]}|
|
||||
|
||||
would be the wrong way to refer to the grammar of a function call,
|
||||
because it produces @scheme[(rator-expr rand-expr ...)], where
|
||||
@schemeidfont{rator-expr} and @schemeidfont{rand-expr} are
|
||||
typeset as variables. The correct description is
|
||||
|
||||
@verbatim[" @scheme[(_rator-expr _rand-expr ...)]"]
|
||||
@verbatim|{ @scheme[(_rator-expr _rand-expr ...)]}|
|
||||
|
||||
which produces @scheme[(_rator-expr _rand-expr ...)], where
|
||||
@schemeidfont{rator-expr} @schemeidfont{rand-expr} are typeset as
|
||||
|
|
|
@ -34,7 +34,7 @@ offset of an embedded pict in a larger pict.
|
|||
In addition to its drawing part, a pict has the following
|
||||
@deftech{bounding box} structure:
|
||||
|
||||
@verbatim[#<<EOS
|
||||
@verbatim{
|
||||
w
|
||||
------------------
|
||||
| | a \
|
||||
|
@ -42,9 +42,8 @@ In addition to its drawing part, a pict has the following
|
|||
| | | h
|
||||
|----------last----| |
|
||||
| | d /
|
||||
------------------
|
||||
EOS
|
||||
]
|
||||
------------------
|
||||
}
|
||||
|
||||
That is, the bounding box has a width @math{w} and a height
|
||||
@math{h}. For a single text line, @math{d} is descent below the
|
||||
|
|
Loading…
Reference in New Issue
Block a user