1 <!-- $Id: frontend.xml,v 1.23 2004-06-15 09:33:12 adam Exp $ -->
2 <chapter id="server"><title>Generic server</title>
3 <sect1><title>Introduction</title>
6 If you aren't into documentation, a good way to learn how the
7 back end interface works is to look at the <filename>backend.h</filename>
8 file. Then, look at the small dummy-server in
9 <filename>ztest/ztest.c</filename>. The <filename>backend.h</filename>
10 file also makes a good reference, once you've chewed your way through
11 the prose of this file.
15 If you have a database system that you would like to make available by
16 means of Z39.50, SRW o SRU, &yaz; basically offers your two options. You
17 can use the APIs provided by the &asn;, &odr;, and &comstack;
19 create and decode PDUs, and exchange them with a client.
20 Using this low-level interface gives you access to all fields and
21 options of the protocol, and you can construct your server as close
22 to your existing database as you like.
23 It is also a fairly involved process, requiring
24 you to set up an event-handling mechanism, protocol state machine,
25 etc. To simplify server implementation, we have implemented a compact
26 and simple, but reasonably full-functioned server-frontend that will
27 handle most of the protocol mechanics, while leaving you to
28 concentrate on your database interface.
33 The backend interface was designed in anticipation of a specific
34 integration task, while still attempting to achieve some degree of
35 generality. We realize fully that there are points where the
36 interface can be improved significantly. If you have specific
37 functions or parameters that you think could be useful, send us a
38 mail (or better, sign on to the mailing list referred to in the
39 top-level README file). We will try to fit good suggestions into future
40 releases, to the extent that it can be done without requiring
41 too many structural changes in existing applications.
47 The &yaz; server does not support XCQL.
52 <sect1 id="server.frontend"><title>The Database Frontend</title>
55 We refer to this software as a generic database frontend. Your
56 database system is the <emphasis>backend database</emphasis>, and the
57 interface between the two is called the <emphasis>backend API</emphasis>.
58 The backend API consists of a small number of function handlers and
59 structure definitions. You are required to provide the
60 <function>main()</function> routine for the server (which can be
61 quite simple), as well as a set of handlers to match each of the
63 The interface functions that you write can use any mechanism you like
64 to communicate with your database system: You might link the whole
65 thing together with your database application and access it by
66 function calls; you might use IPC to talk to a database server
67 somewhere; or you might link with third-party software that handles
68 the communication for you (like a commercial database client library).
69 At any rate, the handlers will perform the tasks of:
87 Scanning the database index (optional - if you wish to implement SCAN).
91 Extended Services (optional).
95 Result-Set Delete (optional).
99 Result-Set Sort (optional).
105 (more functions will be added in time to support as much of
106 Z39.50-1995 as possible).
110 <sect1 id="server.backend"><title>The Backend API</title>
113 The header file that you need to use the interface are in the
114 <filename>include/yaz</filename> directory. It's called
115 <filename>backend.h</filename>. It will include other files from
116 the <filename>include/yaz</filename> directory, so you'll
117 probably want to use the -I option of your compiler to tell it
118 where to find the files. When you run
119 <literal>make</literal> in the top-level &yaz; directory,
120 everything you need to create your server is to link with the
121 <filename>lib/libyaz.la</filename> library.
125 <sect1 id="server.main"><title>Your main() Routine</title>
128 As mentioned, your <function>main()</function> routine can be quite brief.
129 If you want to initialize global parameters, or read global configuration
130 tables, this is the place to do it. At the end of the routine, you should
135 int statserv_main(int argc, char **argv,
136 bend_initresult *(*bend_init)(bend_initrequest *r),
137 void (*bend_close)(void *handle));
141 The third and fourth arguments are pointers to handlers. Handler
142 <function>bend_init</function> is called whenever the server receives
143 an Initialize Request, so it serves as a Z39.50 session initializer. The
144 <function>bend_close</function> handler is called when the session is
149 <function>statserv_main</function> will establish listening sockets
150 according to the parameters given. When connection requests are received,
151 the event handler will typically <function>fork()</function> and
152 create a sub-process to handle a new connection.
153 Alternatively the server may be setup to create threads for each
155 If you do use global variables and forking, you should be aware, then,
156 that these cannot be shared between associations, unless you explicitly
157 disable forking by command line parameters.
161 The server provides a mechanism for controlling some of its behavior
162 without using command-line options. The function
166 statserv_options_block *statserv_getcontrol(void);
170 will return a pointer to a <literal>struct statserv_options_block</literal>
171 describing the current default settings of the server. The structure
172 contains these elements:
176 <literal>int dynamic</literal></term><listitem><para>
177 A boolean value, which determines whether the server
178 will fork on each incoming request (TRUE), or not (FALSE). Default is
179 TRUE. This flag is only read by UNIX-based servers (WIN32 based servers
181 </para></listitem></varlistentry>
184 <literal>int threads</literal></term><listitem><para>
185 A boolean value, which determines whether the server
186 will create a thread on each incoming request (TRUE), or not (FALSE).
187 Default is FALSE. This flag is only read by UNIX-based servers
188 that offer POSIX Threads support.
189 WIN32-based servers always operate in threaded mode.
190 </para></listitem></varlistentry>
193 <literal>int inetd</literal></term><listitem><para>
194 A boolean value, which determines whether the server
195 will operates under a UNIX INET daemon (inetd). Default is FALSE.
196 </para></listitem></varlistentry>
199 <literal>int loglevel</literal></term><listitem><para>
200 Set this by ORing the constants defined in
201 <filename>include/yaz/yaz-log.h</filename>.
202 </para></listitem></varlistentry>
205 <literal>char logfile[ODR_MAXNAME+1]</literal></term>
206 <listitem><para>File for diagnostic output ("": stderr).
207 </para></listitem></varlistentry>
210 <literal>char apdufile[ODR_MAXNAME+1]</literal></term>
212 Name of file for logging incoming and outgoing APDUs
213 ("": don't log APDUs, "-":
214 <literal>stderr</literal>).
215 </para></listitem></varlistentry>
218 <literal>char default_listen[1024]</literal></term>
219 <listitem><para>Same form as the command-line specification of
220 listener address. "": no default listener address.
221 Default is to listen at "tcp:@:9999". You can only
222 specify one default listener address in this fashion.
223 </para></listitem></varlistentry>
226 <literal>enum oid_proto default_proto;</literal></term>
227 <listitem><para>Either <literal>PROTO_Z3950</literal> or
228 <literal>PROTO_SR</literal>.
229 Default is <literal>PROTO_Z39_50</literal>.
230 </para></listitem></varlistentry>
233 <literal>int idle_timeout;</literal></term>
234 <listitem><para>Maximum session idle-time, in minutes. Zero indicates
235 no (infinite) timeout. Default is 15 minutes.
236 </para></listitem></varlistentry>
239 <literal>int maxrecordsize;</literal></term>
240 <listitem><para>Maximum permissible record (message) size. Default
241 is 1Mb. This amount of memory will only be allocated if a
242 client requests a very large amount of records in one operation
244 Set it to a lower number if you are worried about resource
245 consumption on your host system.
246 </para></listitem></varlistentry>
249 <literal>char configname[ODR_MAXNAME+1]</literal></term>
250 <listitem><para>Passed to the backend when a new connection is received.
251 </para></listitem></varlistentry>
254 <literal>char setuid[ODR_MAXNAME+1]</literal></term>
255 <listitem><para>Set user id to the user specified, after binding
256 the listener addresses.
257 </para></listitem></varlistentry>
260 <literal>void (*bend_start)(struct statserv_options_block *p)</literal>
262 <listitem><para>Pointer to function which is called after the
263 command line options have been parsed - but before the server
265 For forked UNIX servers this handler is called in the mother
266 process; for threaded servers this handler is called in the
268 The default value of this pointer is NULL in which case it
269 isn't invoked by the frontend server.
270 When the server operates as an NT service this handler is called
271 whenever the service is started.
272 </para></listitem></varlistentry>
275 <literal>void (*bend_stop)(struct statserv_options_block *p)</literal>
277 <listitem><para>Pointer to function which is called whenever the server
278 has stopped listening for incoming connections. This function pointer
279 has a default value of NULL in which case it isn't called.
280 When the server operates as an NT service this handler is called
281 whenever the service is stopped.
282 </para></listitem></varlistentry>
285 <literal>void *handle</literal></term>
286 <listitem><para>User defined pointer (default value NULL).
287 This is a per-server handle that can be used to specify "user-data".
288 Do not confuse this with the session-handle as returned by bend_init.
289 </para></listitem></varlistentry>
295 The pointer returned by <literal>statserv_getcontrol</literal> points to
296 a static area. You are allowed to change the contents of the structure,
297 but the changes will not take effect before you call
301 void statserv_setcontrol(statserv_options_block *block);
306 that you should generally update this structure before calling
307 <function>statserv_main()</function>.
312 <sect1 id="server.backendfunctions"><title>The Backend Functions</title>
315 For each service of the protocol, the backend interface declares one or
316 two functions. You are required to provide implementations of the
317 functions representing the services that you wish to implement.
320 <sect2><title>Init</title>
323 bend_initresult (*bend_init)(bend_initrequest *r);
327 This handler is called once for each new connection request, after
328 a new process/thread has been created, and an Initialize Request has
329 been received from the client. The pointer to the
330 <function>bend_init</function> handler is passed in the call to
331 <function>statserv_start</function>.
335 This handler is also called when operating in SRW/SRU mode - when
336 a connection has been made (even though SRW/SRU does not offer
341 Unlike previous versions of YAZ, the <function>bend_init</function> also
342 serves as a handler that defines the Z39.50 services that the backend
343 wish to support. Pointers to <emphasis>all</emphasis> service handlers,
344 including search - and fetch must be specified here in this handler.
347 The request - and result structures are defined as
351 typedef struct bend_initrequest
353 Z_IdAuthentication *auth;
354 ODR stream; /* encoding stream */
355 ODR print; /* printing stream */
356 Z_ReferenceId *referenceId;/* reference ID */
357 char *peer_name; /* dns host of peer (client) */
359 char *implementation_id;
360 char *implementation_name;
361 char *implementation_version;
362 int (*bend_sort) (void *handle, bend_sort_rr *rr);
363 int (*bend_search) (void *handle, bend_search_rr *rr);
364 int (*bend_fetch) (void *handle, bend_fetch_rr *rr);
365 int (*bend_present) (void *handle, bend_present_rr *rr);
366 int (*bend_esrequest) (void *handle, bend_esrequest_rr *rr);
367 int (*bend_delete)(void *handle, bend_delete_rr *rr);
368 int (*bend_scan)(void *handle, bend_scan_rr *rr);
369 int (*bend_segment)(void *handle, bend_segment_rr *rr);
371 ODR decode; /* decoding stream */
372 /* character set and language negotiation - see include/yaz/z-charneg.h */
373 Z_CharSetandLanguageNegotiation *charneg_request;
374 Z_External *charneg_response;
377 typedef struct bend_initresult
379 int errcode; /* 0==OK */
380 char *errstring; /* system error string or NULL */
381 void *handle; /* private handle to the backend module */
386 In general, the server frontend expects that the
387 <literal>bend_*result</literal> pointer that you return is valid at
388 least until the next call to a <literal>bend_* function</literal>.
389 This applies to all of the functions described herein. The parameter
390 structure passed to you in the call belongs to the server frontend, and
391 you should not make assumptions about its contents after the current
392 function call has completed. In other words, if you want to retain any
393 of the contents of a request structure, you should copy them.
397 The <literal>errcode</literal> should be zero if the initialization of
398 the backend went well. Any other value will be interpreted as an error.
399 The <literal>errstring</literal> isn't used in the current version, but
400 one option would be to stick it in the initResponse as a VisibleString.
401 The <literal>handle</literal> is the most important parameter. It should
402 be set to some value that uniquely identifies the current session to
403 the backend implementation. It is used by the frontend server in any
404 future calls to a backend function.
405 The typical use is to set it to point to a dynamically allocated state
406 structure that is private to your backend module.
410 The <literal>auth</literal> member holds the authentication information
411 part of the Z39.50 Initialize Request. Interpret this if your serves
412 requires authentication.
416 The members <literal>peer_name</literal>,
417 <literal>implementation_id</literal>,
418 <literal>implementation_name</literal> and
419 <literal>implementation_version</literal> holds
420 DNS of client, ID of implementor, name
421 of client (Z39.50) implementation - and version.
425 The <literal>bend_</literal> - members are set to NULL when
426 <function>bend_init</function> is called. Modify the pointers by
427 setting them to point to backend functions.
432 <sect2><title>Search and retrieve</title>
434 <para>We now describe the handlers that are required to support search -
435 and retrieve. You must support two functions - one for search - and one
436 for fetch (retrieval of one record). If desirable you can provide a
437 third handler which is called when a present request is received which
438 allows you to optimize retrieval of multiple-records.
442 int (*bend_search) (void *handle, bend_search_rr *rr);
445 char *setname; /* name to give to this set */
446 int replace_set; /* replace set, if it already exists */
447 int num_bases; /* number of databases in list */
448 char **basenames; /* databases to search */
449 Z_ReferenceId *referenceId;/* reference ID */
450 Z_Query *query; /* query structure */
451 ODR stream; /* encode stream */
452 ODR decode; /* decode stream */
453 ODR print; /* print stream */
455 bend_request request;
456 bend_association association;
458 int hits; /* number of hits */
459 int errcode; /* 0==OK */
460 char *errstring; /* system error string or NULL */
465 The <function>bend_search</function> handler is a fairly close
466 approximation of a protocol Z39.50 Search Request - and Response PDUs
467 The <literal>setname</literal> is the resultSetName from the protocol.
468 You are required to establish a mapping between the set name and whatever
469 your backend database likes to use.
470 Similarly, the <literal>replace_set</literal> is a boolean value
471 corresponding to the resultSetIndicator field in the protocol.
472 <literal>num_bases/basenames</literal> is a length of/array of character
473 pointers to the database names provided by the client.
474 The <literal>query</literal> is the full query structure as defined in
475 the protocol ASN.1 specification.
476 It can be either of the possible query types, and it's up to you to
477 determine if you can handle the provided query type.
478 Rather than reproduce the C interface here, we'll refer you to the
479 structure definitions in the file
480 <filename>include/yaz/z-core.h</filename>. If you want to look at the
481 attributeSetId OID of the RPN query, you can either match it against
482 your own internal tables, or you can use the
483 <literal>oid_getentbyoid</literal> function provided by &yaz;.
487 The structure contains a number of hits, and an
488 <literal>errcode/errstring</literal> pair. If an error occurs
489 during the search, or if you're unhappy with the request, you should
490 set the errcode to a value from the BIB-1 diagnostic set. The value
491 will then be returned to the user in a nonsurrogate diagnostic record
492 in the response. The <literal>errstring</literal>, if provided, will
493 go in the addinfo field. Look at the protocol definition for the
494 defined error codes, and the suggested uses of the addinfo field.
498 The <function>bend_search</function> handler is also called when
499 the frontend server receives a SRW/SRU SearchRetrieveRequest.
500 For SRW/SRU, a CQL query is usually provided by the client.
501 The CQL query is available as part of <literal>Z_Query</literal>
502 structure (note that CQL is now part of Z39.50 via an external).
503 To support CQL in existing implementations that only do Type-1,
504 we refer to the CQL-to-PQF tool described
505 <link linkend="tools.cql.pqf">here</link>.
509 To maintain backwards compatibility, the frontend server
510 of yaz always assume that error codes are BIB-1 diagnostics.
511 For SRW/SRU operation, a Bib-1 diagnostic code is mapped to
516 int (*bend_fetch) (void *handle, bend_fetch_rr *rr);
518 typedef struct bend_fetch_rr {
519 char *setname; /* set name */
520 int number; /* record number */
521 Z_ReferenceId *referenceId;/* reference ID */
522 oid_value request_format; /* One of the CLASS_RECSYN members */
523 int *request_format_raw; /* same as above (raw OID) */
524 Z_RecordComposition *comp; /* Formatting instructions */
525 ODR stream; /* encoding stream - memory source if req */
526 ODR print; /* printing stream */
528 char *basename; /* name of database that provided record */
529 int len; /* length of record or -1 if structured */
530 char *record; /* record */
531 int last_in_set; /* is it? */
532 oid_value output_format; /* format */
533 int *output_format_raw; /* used instead of above if not-null */
534 int errcode; /* 0==success */
535 char *errstring; /* system error string or NULL */
536 int surrogate_flag; /* surrogate diagnostic */
537 char *schema; /* string record schema input/output */
542 The frontend server calls the <function>bend_fetch</function> handler
543 when it needs database records to fulfill a Z39.50 Search Request, a
544 Z39.50 Present Request or a SRW SearchRetrieveRequest.
545 The <literal>setname</literal> is simply the name of the result set
546 that holds the reference to the desired record.
547 The <literal>number</literal> is the offset into the set (with 1
548 being the first record in the set). The <literal>format</literal> field
549 is the record format requested by the client (See
550 <xref linkend="asn.oid"/>).
551 The value <literal>VAL_NONE</literal> indicates that the client did
552 not request a specific format. The <literal>stream</literal> argument
553 is an &odr; stream which should be used for
554 allocating space for structured data records.
555 The stream will be reset when all records have been assembled, and
556 the response package has been transmitted.
557 For unstructured data, the backend is responsible for maintaining a
558 static or dynamic buffer for the record between calls.
562 If a SRW/SRU SearchRetrieveRequest is received by the frontend server,
563 the <literal>referenceId</literal> is NULL and the
564 <literal>request_format</literal> (transfer syntax) is XML (OID name
565 <literal>VAL_TEXT_XML</literal>).
566 The schema for SRW/SRU is stored in both the
567 <literal>Z_RecordComposition</literal>
568 structure and <literal>schema</literal> (simple string).
572 In the structure, the <literal>basename</literal> is the name of the
573 database that holds the
574 record. <literal>len</literal> is the length of the record returned, in
575 bytes, and <literal>record</literal> is a pointer to the record.
576 <literal>last_in_set</literal> should be nonzero only if the record
577 returned is the last one in the given result set.
578 <literal>errcode</literal> and <literal>errstring</literal>, if
579 given, will be interpreted as a global error pertaining to the
580 set, and will be returned in a non-surrogate-diagnostic.
581 If you wish to return the error as a surrogate-diagnostic
582 (local error) you can do this by setting
583 <literal>surrogate_flag</literal> to 1 also.
587 If the <literal>len</literal> field has the value -1, then
588 <literal>record</literal> is assumed to point to a constructed data
589 type. The <literal>format</literal> field will be used to determine
590 which encoder should be used to serialize the data.
595 If your backend generates structured records, it should use
596 <function>odr_malloc()</function> on the provided stream for allocating
597 data: This allows the frontend server to keep track of the record sizes.
602 The <literal>format</literal> field is mapped to an object identifier
603 in the direct reference of the resulting EXTERNAL representation
609 The current version of &yaz; only supports the direct reference mode.
614 int (*bend_present) (void *handle, bend_present_rr *rr);
617 char *setname; /* set name */
619 int number; /* record number */
620 oid_value format; /* One of the CLASS_RECSYN members */
621 Z_ReferenceId *referenceId;/* reference ID */
622 Z_RecordComposition *comp; /* Formatting instructions */
623 ODR stream; /* encoding stream */
624 ODR print; /* printing stream */
625 bend_request request;
626 bend_association association;
628 int hits; /* number of hits */
629 int errcode; /* 0==OK */
630 char *errstring; /* system error string or NULL */
635 The <function>bend_present</function> handler is called when
636 the server receives a Z39.50 Present Request.
637 The <literal>setname</literal>,
638 <literal>start</literal> and <literal>number</literal> is the
639 name of the result set - start position - and number of records to
640 be retrieved respectively. <literal>format</literal> and
641 <literal>comp</literal> is the preferred transfer syntax and element
642 specifications of the present request.
645 Note that this is handler serves as a supplement for
646 <function>bend_fetch</function> and need not to be defined in order to
647 support search - and retrieve.
652 <sect2><title>Delete</title>
655 For back-ends that supports delete of a result set only one handler
660 int (*bend_delete)(void *handle, bend_delete_rr *rr);
662 typedef struct bend_delete_rr {
666 Z_ReferenceId *referenceId;
667 int delete_status; /* status for the whole operation */
668 int *statuses; /* status each set - indexed as setnames */
676 The delete set function definition is rather primitive, mostly because
677 we have had no practical need for it as of yet. If someone wants
678 to provide a full delete service, we'd be happy to add the
679 extra parameters that are required. Are there clients out there
680 that will actually delete sets they no longer need?
686 <sect2><title>scan</title>
689 For servers that wish to offer the scan service one handler
694 int (*bend_delete)(void *handle, bend_delete_rr *rr);
697 BEND_SCAN_SUCCESS, /* ok */
698 BEND_SCAN_PARTIAL /* not all entries could be found */
701 typedef struct bend_scan_rr {
702 int num_bases; /* number of elements in database list */
703 char **basenames; /* databases to search */
704 oid_value attributeset;
705 Z_ReferenceId *referenceId; /* reference ID */
706 Z_AttributesPlusTerm *term;
707 ODR stream; /* encoding stream - memory source if required */
708 ODR print; /* printing stream */
710 int *step_size; /* step size */
711 int term_position; /* desired index of term in result list/returned */
712 int num_entries; /* number of entries requested/returned */
714 struct scan_entry *entries;
715 bend_scan_status status;
723 <sect1 id="server.invocation"><title>Application Invocation</title>
726 The finished application has the following
727 invocation syntax (by way of <function>statserv_main()</function>):
731 <command>appname</command>
732 <arg choice="opt"><option>-install</option></arg>
733 <arg choice="opt"><option>-installa</option></arg>
734 <arg choice="opt"><option>-remove</option></arg>
735 <arg choice="opt"><option>-a <replaceable>file</replaceable></option></arg>
736 <arg choice="opt"><option>-v <replaceable>level</replaceable></option></arg>
737 <arg choice="opt"><option>-l <replaceable>file</replaceable></option></arg>
738 <arg choice="opt"><option>-u <replaceable>uid</replaceable></option></arg>
739 <arg choice="opt"><option>-c <replaceable>config</replaceable></option></arg>
740 <arg choice="opt"><option>-t <replaceable>minutes</replaceable></option></arg>
742 <arg choice="opt"><option>-k <replaceable>kilobytes</replaceable></option></arg>
743 <arg choice="opt"><option>-d <replaceable>daemon</replaceable></option></arg>
744 <arg choice="opt"><option>-w <replaceable>dir</replaceable></option></arg>
745 <arg choice="opt"><option>-p <replaceable>pidfile</replaceable></option></arg>
746 <arg choice="opt"><option>-ziDST1</option></arg>
747 <arg choice="opt" rep="repeat">listener-spec</arg>
758 A listener specification consists of a transport mode followed by a
759 colon (:) followed by a listener address. The transport mode is
760 either <literal>tcp</literal>, <literal>unix:</literal> or
761 <literal>ssl</literal>.
765 For TCP and SSL, an address has the form
769 hostname | IP-number [: portnumber]
773 The port number defaults to 210 (standard Z39.50 port).
777 For UNIX, the address is the filename of socket.
781 For TCP/IP and SSL, the special hostname <literal>@</literal>
782 (at sign) is mapped to the address <literal>INADDR_ANY</literal>,
783 which causes the server to listen on any local interface.
786 <example><title>Running the GFS on Unix</title>
788 Assuming the server application <replaceable>appname</replaceable> is
789 started as root, the following will make it listen on port 210.
790 The server will change identity to <literal>nobody</literal>
791 and write its log to <filename>/var/log/app.log</filename>.
793 <replaceable>appname</replaceable> -l /var/log/app.log -u nobody tcp:@:210
797 The server will accept Z39.50 requests and offer SRW/SRU service
801 <example><title>Setting up Apache as SRW/SRU Frontend</title>
803 If you use <ulink url="http://httpd.apache.org/">Apache</ulink>
804 as your public web server and want to offer HTTP port 80
805 access to the YAZ server on 210, you can use the
806 <ulink url="http://httpd.apache.org/docs/mod/mod_proxy.html#proxypass">
807 <literal>ProxyPass</literal></ulink>
809 If you have virtual host
810 <literal>srw.mydomain</literal> you can use the following directives
811 in Apache's httpd.conf:
814 ErrorLog /home/srw/logs/error_log
815 TransferLog /home/srw/logs/access_log
816 ProxyPass / http://srw.mydomain:210/
821 The above for the Apache 1.3 series.
824 <example><title>Running a server with local access only</title>
826 Servers that is only being accessed from the local host should listen
827 on UNIX file socket rather than a Internet socket. To listen on
828 <filename>/tmp/mysocket</filename> start the server as follows:
830 <replaceable>appname</replaceable> tcp:/tmp/mysocket
837 <!-- Keep this comment at the end of the file
842 sgml-minimize-attributes:nil
843 sgml-always-quote-attributes:t
846 sgml-parent-document: "yaz.xml"
847 sgml-local-catalogs: nil
848 sgml-namecase-general:t