.\" This file is part of cfpeek -*- nroff -*- .\" Copyright (C) 2011, 2012 Sergey Poznyakoff .\" .\" Cfpeek is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 3, or (at your option) .\" any later version. .\" .\" Cfpeek is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with cfpeek. If not, see . .\" .TH CFPEEK 1 "August 14, 2012" "CFPEEK" "Cfpeek User Reference" .SH NAME cfpeek \- retrieve values from a structured configuration file .SH SYNOPSIS .B cfpeek [\fIOPTION\fR]... \fIFILE\fR \fR[\fIPATH\fR...] .SH DESCRIPTION Structured configuration files keep program configuration values in a hierarchical structure. Examples of such files are .BR mailutils.conf , used by \fBGNU Mailutils\fR, .BR named.conf , used by \fBnamed\fR, or .BR ~/.gitconfig , used by \fBGIT\fR. .PP .B Cfpeek provides a uniform and consistent command line interface for various operations on such files. It can be used to retrieve configuration values, both by literal keyword matches and by using wildcard patterns, to reformat the files in arbitrary way, and to apply an external script to each statement in the file. .PP This manpage is a short description of \fBcfpeek\fR. For a detailed discussion, including examples of the configuration and usage recommendation, refer to the \fBCfpeek User Manual\fR available in Texinfo format. To access it, run: .PP \fBinfo cfpeek\fR .PP Should any discrepancies occur between this manpage and the \fBCfpeek User Manual\fR, the later shall be considered the authoritative source. .SS Input formats .B Cfpeek is able to handle input files in several formats: .TP .B GRECS The default input format. This format is used, e.g., by \fBGNU Dico\fR, \fBGNU Mailutils\fR, \fBMailfromd\fR and others. The following is a short description. See .BR grecs_config (5), for a detailed discussion of the format and its features. .sp The \fBGRECS\fR input file consists of .I simple and .B block statements. Simple statements consist of a keyword and value, separated by any amount of whitespace. Simple statements are terminated with a semicolon, e.g.: .sp .nf .in +2 wakeup 15; .in .fi .sp A value can be either a single scalar value, as in the example above, or several values, separated by whitespace, or a \fIlist of values\fR: a comma-separated list, enclosed in a pair of braces, e.g.: .sp .nf .in +2 path ("/usr","/usr/bin"); .in .fi .sp Block statements are used for logical grouping of other statements. A block statement consists of a keyword, optionally followed by a value, and a set of other statements, enclosed in a pair of curly brackets, e.g.: .sp .nf .in +2 syslog { facility local1; tag foo; } .in .fi .sp A semicolon may follow the closing \fB}\fR, although this is not required. .sp The whitespace (i.e. space characters, tabs and newlines) has no special syntactical meaning, except that it serves to separate otherwise adjacent tokens. .sp Several types of comments are supported. A single-line comment starts with a \fB#\fR or \fB//\fR and continues to the end of the line. A multi-line comment starts with the two characters \fB/*\fR (slash, star) and continues until the first occurrence of \fB*/\fR (star, slash). Whatever comment type are used, they are removed from the configuration prior to parsing it. .sp After comment removal, the Grecs configuration is normally preprocessed using \fBm4\fR. .TP .B PATH A line-oriented \*(lqkeyword path\*(rq format. It is similar to X-resource format, used by \fBX\fR window system configuration files. Each line consists of a keyword pathname, followed by a semicolon, a single space character and the value of that keyword. .sp Comments (introduced with a \fB#\fR character) and empty lines are ignored. .sp This format is used by default in \fBcfpeek\fR output. .TP .B BIND The format used by \fBnamed\fR DNS daemon. It is similar to \fBGRECS\fR, except that it requires the use of semicolon after block statements, does not support list values and here-document syntax, etc. See .BR named.conf (5), for a detailed description. .TP .B DHCPD The format used by \fBdhcpd\fR daemon. It is similar to \fBBIND\fR, with a few exceptions. See .BR dhcpd.conf (5), for a detailed description. .TP .B META1 The format used by \fBMeTA1\fR configuration file. Similar to \fBGRECS\fR, in some aspects. The most prominent differences are: the use of the equals sign between the keyword and its value and the use of curly brackets to enclose list values. Only one-line comments are allowed, the \fB#\fR serves as a comment starter. See .B http://www.meta1.org, for a detailed description. .TP .B GIT The format used by \fBgit\fR configuration files. See the section \fBCONFIGURATION FILE\fR in .BR git-config (1). .SS Full dump When used with a single non-optional argument, \fIFILE\fR, \fBcfpeek\fR parses that file and outputs its contents in a so called \fBpath-value\fR form. Each line of this output consists of a \fBfull keyword pathname\fR, followed by a semicolon, a single space and a value. .PP The \fBfull keyword pathname\fR identifies that particular statement within the file. It is similar to UNIX file pathname, and consists of the names of all sections (block statements) within which the keyword appears. A dot is used as path component separator. For example, given the following (\fBGRECS\fR-style) configuration file: .sp .nf .in +2 foo { bar { baz 45; # \fBA\fR. } baz 98; # \fBB\fR. } .fi .PP The full pathname of the statement marked with \fBA\fR can be written as: .sp .nf .in +2 .foo.bar.baz .fi .PP Similarly, the statement marked with \fBB\fR has the following pathname: .sp .nf .in +2 .foo.baz .fi .PP A block statement that has a tag is referred to by its name, followed by an equals sign, followed by the tag value. For example, the statement \fBA\fR in the file below: .sp .nf .in +2 program x { bar { baz 45; # \fBA\fR. } } .fi .PP is identified by the following pathname: .sp .nf .in +2 .program=x.bar.baz .fi .PP The tag can optionally be enclosed in a pair of double quotes. Such a quoting becomes mandatory for tags that contain white space or path component separator, e.g.: .sp .nf .in +2 .program=\(dqa.out\(dq.bar.baz .fi .PP See .BR grecs_stmt_path (5), for a detailed description of this format. .SS Keyword lookup Any additional arguments, if specified, are treated as pathnames of the keywords to find in the file. For each additional argument (hereinafter referred to as \fBsearch key\fR), the corresponding keyword is looked up in the configuration and output in the path-value form. All possible matches are shown, unless limited by the .B \-\-matches option (see below). .PP Any number of search keys can be given in the command line. If a search key produces no matches, a diagnostic message is issued unless the \fB\-q\fR (\fB\-\-quiet\fR) has been given, which suppresses this behavior. .PP By default, \fBwildcard match\fR is assumed. A \fB%\fR character in place of a keyword matches any single keyword. Thus: .sp .nf .in +2 cfpeek file.conf .%.bar.baz .fi .PP will match \fB.foo.bar.baz\fR, \fB.qux.bar.baz\fR, but will not match \fB.bar.baz\fR or \fB.x.y.bar.baz\fR. .PP A single \fB*\fR character in place of a keyword matches zero or more keywords appearing in its place, so that .sp .nf .in +2 cfpeek file.conf .*.bar.baz .fi .PP would match all pathnames listed above. .PP Block statement tags are matched using the traditional globbing patterns (see .BR fnmatch (3) ), e.g.: .sp .nf .in +2 cfpeek file.conf .*.program="mh-*" .fi .PP will match any \fBprogram\fR block statement whose tag begins with \*(lqmh-\*(rq. .PP The literal matching can be requested with the \fB\-L\fR (\fB\-\-literal\fR) option. .SS Scripting The scripting mode is enabled when at least one of .BR \-f " (" \-\-file ) or .BR \-e " (" \-\-expression ) options is given. This is a highly useful advanced feature, which allows you to apply programs of arbitrary complexity to each node of the parsing tree. For a detailed discussion, see the section .BR SCRIPTING . .SH OPTIONS .SS Output control .TP \fB\-H\fR, \fB\-\-format\fR=\fIFLAGS\fR Sets output format flags. \fIFLAGS\fR is a comma-separated list of one or more of the following: .sp .RS .PD 0 .TP \fBparent\fR=\fINAME\fR Search for a parent node with the given \fINAME\fR and print it. .TP \fBchild\fR=\fINAME\fR Locate a child of the current node which has the identifier \fINAME\fR and print it. .TP \fBsibling\fR=\fINAME\fR Find in the current node the first sibling identified by \fINAME\fR. See the example \fB7\fR in the .B EXAMPLES section below. .TP \fBup\fR=\fIN\fR Print \fIN\fRth parent node. .TP \fBdown\fR=\fIN\fR Print the node located \fIN\fR levels of hierarchy below the current one. .TP \fBdelim\fR=\fICHAR\fR Use \fICHAR\fR as path component delimiter, instead of the default dot. .PP The flags below are boolean. When prefixed with \fBno\fR they have the meaning opposite to the described. .TP .B [no]locus Print source location of each configuration statement. A location is printed as the file name, followed by a semicolon, followed by the line number and another semicolon. Locations are separated from the rest of output by a single space character. .TP .B [no]path Print the statement \fIpath\fR. See .BR grecs_stmt_path(5), for a description. If printed, the path is separated from the rest of output on its right by a semicolon and a space. .TP .B [no]value Print the statement value. .TP .B [no]quote Always quote values. By default, the value will be quoted only when necessary, i.e. if it contains white space, quotes or special characters. .TP .B [no]never\-quote Never quote values. .TP .B [no]quote\-hex Print non-printable characters as C hex escapes. This option is ignored if \fBnoquote\fR is set. .TP .B [no]descend Descend into subnodes. .TP .B [no]default Set default options. This is equivalent to: .sp .ti +5 delim=.,path,value,quote .PD .LP .RE .TP \fB\-q\fR, \fB\-\-quiet\fR Suppress error diagnostics. .SS Modifiers .TP \fB\-L\fR, \fB\-\-literal\fR Use literal matching. .TP \fB\-S\fR, \fB\-\-sort\fR sort parse tree lexicographically .TP \fB\-m\fR, \fB\-\-matches\fR=\fINUMBER\fR output at most NUMBER matches .TP \fB\-p\fR, \fB\-\-parser\fR=\fITYPE\fR Select input parser type. Valid values for \fITYPE\fR are: .sp .RS .PD 0 .TP .B GRECS File format used by the \fBGrecs\fR library. This format is used, e.g., by \fBGNU Dico\fR, \fBGNU Mailutils\fR, \fBMailfromd\fR and others. This is the default. See .BR grecs_config (5), for a detailed description. .TP .B META1 Format used by \fBMeTA1\fR configuration file. See .B http://www.meta1.org. .TP .B BIND Format used by \fBnamed\fR. See .BR named.conf (5) .B DHCPD Format used by \fBdhcpd\fR. See .BR dhcpd.conf (5) .TP .B GIT Format used by \fBgit\fR configuration files. See the section \fBCONFIGURATION FILE\fR in .BR git-config (1). .TP .B PATH Keyword path format. .PD .RE .TP \fB\-r\fR, \fB\-\-reduce\fR Reduce the parse tree, so that each keyword occurs no more than once at each tree level. .TP \fB\-s\fR, \fB\-\-set\fR=\fIPATH=VALUE\fR Set a keyword \fIPATH\fR to \fIVALUE\fR. This will be reflected in the output, .SS Scripting .TP \fB\-e\fR, \fB\-\-expression\fR=\fIEXPRESSION\fR Apply this Scheme expression to each node found. .TP \fB\-f\fR, \fB\-\-file\fR=\fIFILE\fR Apply the Scheme script \fIFILE\fR to each node found. The script must define the function \fB(cfpeek node)\fR. See the section .B SCRIPTING, for a detailed description. If both \fB\-\-file\fR and \fB\-\-expression\fR are given, the file is loaded first, and expression is applied to each node. .TP \fB\-i\fR, \fB\-\-init=\fIEXPR\fR This option provides an initialization expression, which is evaluated once, after loading the script file, if one is specified, and before starting the main loop. .TP \fB\-d\fR, \fB\-\-done=\fIEXPR\fR This option supplies a cleanup expression. The expression will be evaluated once, after the main loop finishes. .TP \fB\-l\fR, \fB\-\-lang\fR=\fINAME\fR Select scripting language to use. This is reserved for future use. .SS Preprocessor control \fBGRECS\fR input files are preprocessed by default using \fBm4 -s\fR. \fBBIND\fR input files are preprocessed only if the \fB\-\-preprocessor\fR option (see below) is given, which specifies the preprocessor binary to use. .PP Files in another input formats are never preprocessed. .PP The options below control the preprocessor invocation for those formats that support it. .TP \fB\-D\fR, \fB\-\-define=SYMBOL\fR[=\fIVALUE\fR] Define a preprocessor symbol. .TP \fB\-I\fR, \fB\-\-include\-directory\fR=\fIDIR\fR Add \fIDIR\fR to the list of files searched for include files. .TP \fB\-N\fR, \fB\-\-no\-preprocessor\fR Disable preprocessing. .TP \fB-P\fR, \fB\-\-preprocessor\fR=\fICOMMAND\fR Use \fICOMMAND\fR as a preprocessor, instead of the default \fBm4\fR. .SS Debugging .TP \fB\-X\fR, \fB\-\-debug\-lexer\fR Debug configuration file lexer. .TP \fB\-x\fR, \fB\-\-debug\-parser\fR Debug configuration file parser. .SS Other options .TP \fB\-V\fR, \fB\-\-version\fR Print program version. .TP \fB\-h\fR, \fB\-\-help\fR Print a short option summary. .TP \fB\-\-usage\fR Print a short usage message and a list of available options. .SH SCRIPTING \fBCfpeek\fR enters the scripting mode when at least one of the .BR \-f " (" \-\-file ) or .BR \-e " (" \-\-expression ) options is given. .PP So far only Scheme is supported as a scripting language. .PP The file supplied with the .BR \-f " (" \-\-file ) option must be a valid Scheme source and it must define the function \fBcfpeek\fR as shown in the following example: .sp .nf .in +2 (define (cfpeek node) ...) .in .fi .PP This function will be called for each statement found. The statement itself will be passed to it as \fInode\fR argument. A \fBnode\fR is an opaque data type describing a single node in the parse tree. Each node corresponds to a single statement in the configuration file (be it simple or block statement). It holds all the information necessary to identify this statement: its type (simple or block), location in the source file, identifier (or keyword), value and, if it describes a block statement, pointers to the nodes below it. Nodes form doubly-linked lists both horizontally (i.e. statements on the same nesting level) and vertically (i.e. links between parent block statements and their substatements and vice-versa), so that every node in the tree can be reached from arbitrary another node, and can thus be used as a starting point to traverse the entire tree. .PP A number of functions is provided to access the information stored in a node. These are described below. .PP If the .BR \-e " (" \-\-expression ) option is given, its argument must be a valid Scheme expression. This expression will be evaluated for each statement matching the search criteria. Before evaluating, the global variable \fInode\fR is assigned the tree node describing the statement in question. .PP If both .B \-f and .B \-e options are given, the file supplied by the \fB\-f\fR is loaded first and the expression will be evaluated for each node found. .PP In this case the file does not need to define \fBcfpeek\fR function as it will not be called implicitly, but it may do so, if this function is called from the expression. .PP These options are often used together to supply additional information to the script file. See the example \fB10\fR in the .B EXAMPLES section below. .SS Node functions .TP (\fBgrecs-node?\fR \fIobj\fR) Returns \fB#t\fR if \fIobj\fR is a valid Grecs node. .TP (\fBgrecs-node-root\fR \fInode\fR) Returns the topmost node that can be traced up from \fInode\fR. .TP (\fBgrecs-node-head\fR \fInode\fR) Returns the first node having the same parent and located on the same nesting level as \fInode\fR. I.e. the following always holds true: .sp .nf .in +2 (let ((head (grecs-node-head node))) (and (eq? (grecs-node-up node) (grecs-node-up head)) (not (grecs-node-prev? head)))) .in .fi .TP (\fBgrecs-node-tail\fR \fInode\fR) Returns the last node having the same parent and located on the same nesting level as \fInode\fR. In other words, the following relation is always \fB#t\fR: .sp .nf .in +2 (let ((tail (grecs-node-tail node))) (and (eq? (grecs-node-up node) (grecs-node-up tail)) (not (grecs-node-next? tail)))) .in .fi .TP (\fBgrecs-node-up?\fR \fInode\fR) Returns \fB#t\fR if \fInode\fR has a parent. .TP (\fBgrecs-node-up\fR \fInode\fR) Returns the parent node of \fInode\fR. .TP (\fBgrecs-node-down?\fR \fInode\fR) Returns \fB#t\fR if \fInode\fR has child nodes. .TP (\fBgrecs-node-down\fR \fInode\fR) Returns the first child node of \fInode\fR. .TP (\fBgrecs-node-next?\fR \fInode\fR) Returns \fB#t\fR if \fInode\fR is followed by another node on the same nesting level. .TP (\fBgrecs-node-next\fR \fInode\fR) Returns the node following \fInode\fR on the same nesting level. .TP (\fBgrecs-node-prev?\fR \fInode\fR) Returns \fB#t\fR if \fInode\fR is preceded by another node on the same nesting level. .TP (\fBgrecs-node-prev\fR \fInode\fR) Returns the node preceding \fInode\fR on the same nesting level. .TP (\fBgrecs-node-ident\fR \fInode\fR) Returns the identifier of the \fInode\fR. .TP (\fBgrecs-node-path\fR \fInode\fR) Returns the full path to the \fInode\fR (a string). .TP (\fBgrecs-node-path-list\fR \fInode\fR) Returns the full path to the \fInode\fR, converted to a list. Each list element corresponds to a subnode identifier. A subnode which has a tag is represented by a cons, whose car contains the subnode identifier, and cdr its value. For example, the following path: .sp .nf .in +2 .foo.bar=x.baz .fi .in .sp is represented as .sp .nf .in +2 '(\(dqfoo\(dq (\(dqbar\(dq . \(dqx\(dq) \(dqbaz\(dq) .in .fi .TP (\fBgrecs-node-type\fR \fInode\fR) Returns the type of the \fInode\fR. The following constants are defined: .sp .RS .PD 0 .TP .B grecs-node-root The \fInode\fR is a root node. The following is always \fB#t\fR: .sp .nf .in +2 (and (= (grecs-node-type node) grecs-node-root) (not (grecs-node-up? node)) (not (grecs-node-prev? node))) .in .fi .sp .TP .B grecs-node-stmt The \fInode\fR is a simple statement. The following is always \fB#t\fR: .sp .nf .in +2 (and (= (grecs-node-type node) grecs-node-stmt) (not (grecs-node-down? node))) .in .fi .sp .TP .B grecs-node-block The \fInode\fR is a block statement. .PD .LP .RE .TP (\fBgrecs-node-has-value?\fR \fInode\fR) Returns \fB#t\fR if \fInode\fR has a non-empty value. .TP (\fBgrecs-node-value\fR \fInode\fR) Returns the value of the \fInode\fR. .TP (\fBgrecs-node-locus\fR \fInode\fR) Returns source location of the \fInode\fR. Returned value is a cons: .sp .nf .in +2 (\fBfile-name\fR . \fBline-number\fR) .in .fi .TP (\fBgrecs-find-node\fR \fInode\fR \fIpath\fR) Return the first node whose path, is \fIpath\fR. Start search from \fInode\fR. .TP (\fBgrecs-match-first\fR \fInode\fR \fIpattern\fR) Return the first node whose path matches \fIpattern\fR. Start search from \fInode\fR. .TP (\fBgrecs-match-next\fR \fInode\fR) \fINode\fR must be a node returned by a previous call to \fBgrecs-match-first\fR or \fBgrecs-match-next\fR. The function \fBgrecs-match-next\fR returns next node matching the initial \fIpattern\fR, or \fB#f\fR if no more nodes are found. For example, the following code iterates over all nodes matching \fIpattern\fR: .sp .nf .in +2 (define (iterate-nodes root pattern thunk) (do ((node (grecs-match-first root pattern) (grecs-match-next node))) ((not node)) (thunk node))) .in .fi .SH "EXIT STATUS" On normal termination, the exit status is 0. Exit status 1 indicates that not all search keys has been found. Exit codes greater than 1 indicate various error conditions: .TP 2 Error parsing the input file. .TP 3 Script failure. .TP 64 The command was used incorrectly, e.g., with the wrong number of arguments, a bad option, a bad syntax in a parameter, or whatever. .TP 69 The requested script file does not exist, contains syntax errors, or cannot be parsed for whatever other reason. .TP 70 An internal software error has occurred. Please, report it, along with any error diagnostics produced by the program, if you ever stumble upon this error code. .TP 78 The script file parses correctly, but does not define all the symbols required by .BR cfpeek . .SH EXAMPLES .SS 1. Print the entire contents of the .BR /etc/dico.conf : .P .nf .B $ cfpeek /etc/dico.conf .fi .SS 2. Print contents of .BR /etc/named.conf : .P .nf .B $ cfpeek --parser=bind /etc/named.conf .fi .SS 3. Print the value of the \fBpid-file\fR keyword from the \fBoptions\fR section of .BR /etc/named.conf : .P .nf .B $ cfpeek --parser=bind /etc/named.conf .options.pid-file .fi .P The output will look like: .P .nf .options.pid-file: /var/run/named.pid .fi .P To make it output only the value, without the keyword path, use the .B \-\-format option: .P .nf .B $ cfpeek --parser=bind --format=value /etc/named.conf .options.pid-file .fi .SS 4. This is how to use the above in a startup/shutdown script to bring \fBnamed\fR down: .P .in +5 .nf PIDFILE=`cfpeek --parser=bind --format=value \\ /etc/named.conf .options.pid-file` if [ -R $PIDFILE ] then kill -TERM `head -1 $PIDFILE` fi .fi .in .SS 5. Find and print every occurrence of \fBfacility\fR keyword in .BR /etc/mailutils.rc "(note the quotes):" .P .nf .B $ cfpeek /etc/mailutils.rc '.*.facility' .fi .SS 6. The same, but print only the locations where the keyword occurred: .P .nf .B $ cfpeek --format=locus /etc/mailutils.rc .*.facility .fi .SS 7. Find the names of zone files for all master nodes in the \fBnamed.conf\fR file: .P .nf .B $ cfpeek --parser bind --format=sibling=file,value /etc/named.conf \\\\ .ti +2 .B '.*.zone.type=master' .fi .P See example \fB11\fR below, for another way of doing so. .SS 8. Apply the script \fBmaster-zone.scm\fR to each statement from the file .BR /etc/named.conf: .P .nf .B $ cfpeek --parser=bind --script=master-zone.scm /etc/named.conf . .fi .P Notice the final dot, which refers to the root of the parse tree. .SS 9. Apply the script \fBmaster-zone.scm\fR to each \fBzone\fR statement which occurs within the \fBview \(dqexternal\(dq\fR section: .P .nf .B $ cfpeek --parser=bind --script=master-zone.scm /etc/named.conf \\\\ .ti +2 .B .view=external.zone .fi .SS 10. Same as above, but initialize additional parameters for the script: .P .nf .B $ cfpeek --parser=bind --script=master-zone.scm \\\\ .ti +2 .B --expression '(set! indent 8) (cfpeek node)' /etc/named.conf .ti +2 .B .view=external.zone .fi .P Note the explicit call to \fBcfpeek\fR function. .SS 11. Print the file name of each master zone in the .B /etc/named.conf file: .P .nf .B $ cfpeek --parser=bind -e ' .ti +3 .B (if (and (string=? (grecs-node-ident node) \(dqtype\(dq) .ti +12 .B (string=? (grecs-node-value node) \(dqmaster\(dq)) .ti +7 .B (format #t \(dq~A~%\(dq (grecs-node-value .ti +27 .B (grecs-find-node .ti +29 .B (grecs-node-head node) \(dqfile\(dq))))' \\\\ .ti +10 .B /etc/named.conf .SH "SEE ALSO" .BR grecs_stmt_path (5), .BR grecs_config (5), .BR m4 (1). .PP The full documentation for .B cfpeek is maintained as a Texinfo manual. If the .B info and .B cfpeek programs are properly installed at your site, the command .IP .B info cfpeek .PP should give you access to the complete manual. .PP An online copy of the latest .B cfpeek documentation is available from . .SH AUTHORS Sergey Poznyakoff .SH "BUG REPORTS" Report bugs to . .SH COPYRIGHT Copyright \(co 2011 Sergey Poznyakoff .br .na License GPLv3+: GNU GPL version 3 or later .br .ad This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .\" Local variables: .\" eval: (add-hook 'write-file-hooks 'time-stamp) .\" time-stamp-start: ".TH [A-Z_][A-Z0-9_]* [0-9] \"" .\" time-stamp-format: "%:B %:d, %:y" .\" time-stamp-end: "\"" .\" time-stamp-line-limit: 20 .\" end: