diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
commit | ea28451286d3ea4a772fa174483f9a7a66bb1ab3 (patch) | |
tree | 6ee9d8a7848333a7ceeee3b13d492e40225f8b86 /tcllib/apps | |
parent | b5ca09bae0d6a1edce939eea03594dd56383f2c8 (diff) | |
parent | 7c621da28f07e449ad90c387344f07a453927569 (diff) | |
download | blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.zip blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.gz blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.bz2 |
Merge commit '7c621da28f07e449ad90c387344f07a453927569' as 'tcllib'
Diffstat (limited to 'tcllib/apps')
-rwxr-xr-x | tcllib/apps/dtplite | 28 | ||||
-rw-r--r-- | tcllib/apps/dtplite.man | 447 | ||||
-rwxr-xr-x | tcllib/apps/nns | 291 | ||||
-rw-r--r-- | tcllib/apps/nns.man | 143 | ||||
-rwxr-xr-x | tcllib/apps/nnsd | 153 | ||||
-rw-r--r-- | tcllib/apps/nnsd.man | 91 | ||||
-rwxr-xr-x | tcllib/apps/nnslog | 182 | ||||
-rw-r--r-- | tcllib/apps/nnslog.man | 93 | ||||
-rwxr-xr-x | tcllib/apps/page | 820 | ||||
-rw-r--r-- | tcllib/apps/page.man | 467 | ||||
-rwxr-xr-x | tcllib/apps/pt | 156 | ||||
-rw-r--r-- | tcllib/apps/pt.man | 242 | ||||
-rwxr-xr-x | tcllib/apps/tcldocstrip | 537 | ||||
-rw-r--r-- | tcllib/apps/tcldocstrip.man | 197 |
14 files changed, 3847 insertions, 0 deletions
diff --git a/tcllib/apps/dtplite b/tcllib/apps/dtplite new file mode 100755 index 0000000..6f447bf --- /dev/null +++ b/tcllib/apps/dtplite @@ -0,0 +1,28 @@ +#! /usr/bin/env tclsh +# -*- tcl -*- + +# @@ Meta Begin +# Application dtplite 1.0.5 +# Meta platform tcl +# Meta summary Lightweight DocTools Processor +# Meta description This application is a simple processor +# Meta description for documents written in the doctools +# Meta description markup language. It covers the most +# Meta description common use cases, but is not as +# Meta description configurable as its big brother dtp. +# Meta category Processing doctools documents +# Meta subject doctools doctoc docidx +# Meta require {dtplite 1.0.5} +# Meta author Andreas Kupries +# Meta license BSD +# @@ Meta End + +package require dtplite 1.0.5 + +# dtp lite - Lightweight DocTools Processor +# ======== = ============================== + +exit [dtplite::do $argv] + +# ### ### ### ######### ######### ######### +exit diff --git a/tcllib/apps/dtplite.man b/tcllib/apps/dtplite.man new file mode 100644 index 0000000..8f506e1 --- /dev/null +++ b/tcllib/apps/dtplite.man @@ -0,0 +1,447 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin dtplite n 1.0.5] +[see_also {docidx introduction}] +[see_also {doctoc introduction}] +[see_also {doctools introduction}] +[keywords conversion] +[keywords docidx] +[keywords doctoc] +[keywords doctools] +[keywords HTML] +[keywords manpage] +[keywords markup] +[keywords nroff] +[keywords TMML] +[copyright {2004-2013 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[titledesc {Lightweight DocTools Markup Processor}] +[moddesc {Documentation toolbox}] +[category {Documentation tools}] +[description] +[para] + +The application described by this document, [syscmd dtplite], is the +successor to the extremely simple [syscmd mpexpand]. Influenced in its +functionality by the [syscmd dtp] doctools processor it is much more +powerful than [syscmd mpexpand], yet still as easy to use; definitely +easier than [syscmd dtp] with its myriad of subcommands and options. + +[para] + +[syscmd dtplite] is based upon the package [package doctools], like +the other two processors. + +[subsection {USE CASES}] + +[syscmd dtplite] was written with the following three use cases in +mind. + +[para] +[list_begin enumerated] +[enum] +Validation of a single document, i.e. checking that it was written in +valid doctools format. This mode can also be used to get a preliminary +version of the formatted output for a single document, for display in +a browser, nroff, etc., allowing proofreading of the formatting. + +[enum] +Generation of the formatted documentation for a single package, +i.e. all the manpages, plus a table of contents and an index of +keywords. + +[enum] +An extension of the previous mode of operation, a method for the easy +generation of one documentation tree for several packages, and +especially of a unified table of contents and keyword index. + +[list_end] + +[para] + +Beyond the above we also want to make use of the customization +features provided by the HTML formatter. It is not the only format the +application should be able to generate, but we anticipiate it to be +the most commonly used, and it is one of the few which do provide +customization hooks. + +[para] + +We allow the caller to specify a header string, footer string, a +stylesheet, and data for a bar of navigation links at the top of the +generated document. + +While all can be set as long as the formatting engine provides an +appropriate engine parameter (See section [sectref OPTIONS]) the last +two have internal processing which make them specific to HTML. + +[subsection {COMMAND LINE}] + +[list_begin definitions] + +[call [cmd dtplite] [option -o] [arg output] [opt options] [arg format] [arg inputfile]] + +This is the form for use case [lb]1[rb]. The [arg options] will be +explained later, in section [sectref OPTIONS]. + +[list_begin arguments] + +[arg_def path output in] + +This argument specifies where to write the generated document. It can +be the path to a file or directory, or [const -]. + +The last value causes the application to write the generated +documented to [const stdout]. + +[para] + +If the [arg output] does not exist then [lb]file dirname $output[rb] +has to exist and must be a writable directory. + +The generated document will be written to a file in that directory, +and the name of that file will be derived from the [arg inputfile], +the [arg format], and the value given to option [option -ext] (if +present). + +[arg_def (path|handle) format in] + +This argument specifies the formatting engine to use when processing +the input, and thus the format of the generated document. See section +[sectref FORMATS] for the possibilities recognized by the application. + +[arg_def path inputfile in] + +This argument specifies the path to the file to process. It has to +exist, must be readable, and written in [term doctools] format. + +[list_end] +[para] + +[call [cmd dtplite] [const validate] [arg inputfile]] + +This is a simpler form for use case [lb]1[rb]. The "validate" format +generates no output at all, only syntax checks are performed. As such +the specification of an output file or other options is not necessary +and left out. + +[call [cmd dtplite] [option -o] [arg output] [opt options] [arg format] [arg inputdirectory]] + +This is the form for use case [lb]2[rb]. It differs from the form for +use case [lb]1[rb] by having the input documents specified through a +directory instead of a file. The other arguments are identical, except +for [arg output], which now has to be the path to an existing and +writable directory. + +[para] + +The input documents are all files in [arg inputdirectory] or any of +its subdirectories which were recognized by [cmd fileutil::fileType] +as containing text in [term doctools] format. + +[call [cmd dtplite] [option -merge] [option -o] [arg output] [opt options] [arg format] [arg inputdirectory]] + +This is the form for use case [lb]3[rb]. The only difference to the +form for use case [lb]2[rb] is the additional option [option -merge]. + +[para] + +Each such call will merge the generated documents coming from +processing the input documents under [arg inputdirectory] or any of +its subdirectories to the files under [arg output]. In this manner it +is possible to incrementally build the unified documentation for any +number of packages. Note that it is necessary to run through all the +packages twice to get fully correct cross-references (for formats +supporting them). + +[list_end] + +[subsection OPTIONS] + +This section describes all the options available to the user of the +application, with + +the exception of the options [option -o] and [option -merge]. These +two were described already, in section [sectref {COMMAND LINE}]. + +[para] +[list_begin options] +[opt_def -exclude string] + +This option specifies an exclude (glob) pattern. Any files identified +as manpages to process which match the exclude pattern are +ignored. The option can be provided multiple times, each usage adding +an additional pattern to the list of exclusions. + +[opt_def -ext string] + +If the name of an output file has to be derived from the name of an +input file it will use the name of the [arg format] as the extension +by default. This option here will override this however, forcing it to +use [arg string] as the file extension. This option is ignored if the +name of the output file is fully specified through option [option -o]. + +[para] + +When used multiple times only the last definition is relevant. + +[opt_def -header file] + +This option can be used if and only if the selected [arg format] +provides an engine parameter named "header". It takes the contents of +the specified file and assign them to that parameter, for whatever use +by the engine. The HTML engine will insert the text just after the tag +[const <body>]. + +If navigation buttons are present (see option [option -nav] below), +then the HTML generated for them is appended to the header data +originating here before the final assignment to the parameter. + +[para] + +When used multiple times only the last definition is relevant. + +[opt_def -footer file] + +Like [option -header], except that: Any navigation buttons are ignored, +the corresponding required engine parameter is named "footer", and the +data is inserted just before the tag [const </body>]. + +[para] + +When used multiple times only the last definition is relevant. + +[opt_def -style file] + +This option can be used if and only if the selected [arg format] +provides an engine parameter named "meta". When specified it will +generate a piece of HTML code declaring the [arg file] as the +stylesheet for the generated document and assign that to the +parameter. The HTML engine will insert this inot the document, just +after the tag [const <head>]. + +[para] + +When processing an input directory the stylesheet file is copied into +the output directory and the generated HTML will refer to the copy, to +make the result more self-contained. When processing an input file we +have no location to copy the stylesheet to and so just reference it as +specified. + +[para] + +When used multiple times only the last definition is relevant. + +[opt_def -toc path] + +This option specifies a doctoc file to use for the table of contents +instead of generating our own. + +[para] + +When used multiple times only the last definition is relevant. + +[opt_def -pre+toc "label path|text"] +[opt_def -post+toc "label path|text"] + +This option specifies additional doctoc files (or texts) to use in +the navigation bar. + +[para] Positioning and handling of multiple uses is like for options +[option -prenav] and [option -postnav], see below. + +[opt_def -nav "label url"] +[opt_def -prenav "label url"] + +Use this option to specify a navigation button with [arg label] to +display and the [arg url] to link to. This option can be used if and +only if the selected [arg format] provides an engine parameter named +"header". The HTML generated for this is appended to whatever data we +got from option [option -header] before it is inserted into the +generated documents. + +[para] + +When used multiple times all definitions are collected and a +navigation bar is created, with the first definition shown at the left +edge and the last definition to the right. + +[para] The url can be relative. In that case it is assumed to be relative +to the main files (TOC and Keyword index), and will be transformed for +all others to still link properly. + +[opt_def -postnav "label url"] + +Use this option to specify a navigation button with [arg label] to +display and the [arg url] to link to. This option can be used if and +only if the selected [arg format] provides an engine parameter named +"header". The HTML generated for this is appended to whatever data we +got from option [option -header] before it is inserted into the +generated documents. + +[para] + +When used multiple times all definitions are collected and a +navigation bar is created, with the last definition shown at the right +edge and the first definition to the left. + +[para] The url can be relative. In that case it is assumed to be relative +to the main files (TOC and Keyword index), and will be transformed for +all others to still link properly. + +[list_end] + +[subsection FORMATS] + +At first the [arg format] argument will be treated as a path to a tcl +file containing the code for the requested formatting engine. The +argument will be treated as the name of one of the predefined formats +listed below if and only if the path does not exist. + +[para] + +[emph {Note a limitation}]: If treating the format as path to the tcl +script implementing the engine was sucessful, then this script has to +implement not only the engine API for doctools, i.e. + +[term doctools_api], but for [term doctoc_api] and [term docidx_api] +as well. Otherwise the generation of a table of contents and of a +keyword index will fail. + +[para] + +List of predefined formats, i.e. as provided by the +package [package doctools]: + +[para] +[list_begin definitions] + +[def [const nroff]] + +The processor generates *roff output, the standard format for unix +manpages. + +[def [const html]] + +The processor generates HTML output, for usage in and display by web +browsers. This engine is currently the only one providing the various +engine parameters required for the additional customaization of the +output. + +[def [const tmml]] + +The processor generates TMML output, the Tcl Manpage Markup Language, +a derivative of XML. + +[def [const latex]] + +The processor generates LaTeX output. + +[def [const wiki]] + +The processor generates Wiki markup as understood by [syscmd wikit]. + +[def [const list]] + +The processor extracts the information provided by [cmd manpage_begin]. +[see_also {docidx introduction}] +[see_also {doctoc introduction}] +[see_also {doctools introduction}] +[keywords conversion] +[keywords docidx] +[keywords doctoc] +[keywords doctools] +[keywords HTML] +[keywords manpage] +[keywords markup] +[keywords nroff] +[keywords TMML] + +This format is used internally to extract the meta data from which +both table of contents and keyword index are derived from. + +[def [const null]] + +The processor does not generate any output. This is equivalent to +[const validate]. + +[list_end] + +[subsection {DIRECTORY STRUCTURES}] + +In this section we describe the directory structures generated by the +application under [arg output] when processing all documents in an +[arg inputdirectory]. In other words, this is only relevant to the use +cases [lb]2[rb] and [lb]3[rb]. + +[list_begin definitions] + +[def "[lb]2[rb]"] + +The following directory structure is created when processing a single +set of input documents. The file extension used is for output in +HTML, but that is not relevant to the structure and was just used to +have proper file names. + +[example { + output/ + toc.html + index.html + files/ + path/to/FOO.html +}] + +The last line in the example shows the document +generated for a file FOO located at + +[example { + inputdirectory/path/to/FOO +}] + +[def "[lb]3[rb]"] + +When merging many packages into a unified set of documents the +generated directory structure is a bit deeper: + +[example { + output + .toc + .idx + .tocdoc + .idxdoc + .xrf + toc.html + index.html + FOO1/ + ... + FOO2/ + toc.html + files/ + path/to/BAR.html +}] + +Each of the directories FOO1, ... contains the documents generated for +the package FOO1, ... and follows the structure shown for use case +[lb]2[rb]. The only exception is that there is no per-package index. + +[para] + +The files [file .toc], [file .idx], and [file .xrf] contain the +internal status of the whole output and will be read and updated by +the next invokation. Their contents will not be documented. Remove +these files when all packages wanted for the output have been +processed, i.e. when the output is complete. + +[para] + +The files [file .tocdoc], and [file .idxdoc], are intermediate files +in doctoc and docidx markup, respectively, containing the main table +of contents and keyword index for the set of documents before their +conversion to the chosen output format. + +They are left in place, i.e. not deleted, to serve as demonstrations +of doctoc and docidx markup. + +[list_end] + +[vset CATEGORY doctools] +[include ../modules/doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/apps/nns b/tcllib/apps/nns new file mode 100755 index 0000000..ccf58aa --- /dev/null +++ b/tcllib/apps/nns @@ -0,0 +1,291 @@ +#! /usr/bin/env tclsh +# -*- tcl -*- + +# @@ Meta Begin +# Application nns 1.2 +# Meta platform tcl +# Meta summary Nano Name Service Client +# Meta description This application connects to a name service demon +# Meta description and either registers a name with associated data +# Meta description (until exit) or searches for entries matching a +# Meta description glob pattern. Operations to identify client and +# Meta description server are made available as well. It will survive +# Meta description the loss of the nameserver and automatically reconnect +# Meta description and continue when it comes back (bind and search). +# Meta description +# Meta subject {name service} client +# Meta require {Tcl 8.4} +# Meta require logger +# Meta require nameserv::auto +# Meta require struct::matrix +# Meta author Andreas Kupries +# Meta license BSD +# @@ Meta End + +package provide nns 1.2 + +# nns - Nano Name Service Client +# === = ======================== +# +# Use cases +# --------- +# +# (1) Register something at a nano name service +# (2) Query protocol and feature information. +# (3) Provide application version, and protocol information +# (4) Search service for entries matching a glob-pattern +# +# Command syntax +# -------------- +# +# (Ad 1) nns bind ?-host NAME|IP? ?-port PORT? name data +# (Ad 2) nns ident ?-host NAME|IP? ?-port PORT? +# (Ad 3) nns who +# (Ad 4) nns search ?-host NAME|IP? ?-port PORT? ?-continuous? ?pattern? +# +# Register a name with data. If no port is specified the default +# port 38573 is used to connect to it. If no host is specified +# the default (localhost) is used to connect to it. + +# ### ### ### ######### ######### ######### +## Requirements + +lappend auto_path [file join [file dirname [file dirname \ + [file normalize [info script]]]] modules] + +package require nameserv::auto 0.3 ;# Need auto-restoring search. +package require struct::matrix + +logger::initNamespace ::nns +namespace eval ::nns { log::setlevel info } + +# ### ### ### ######### ######### ######### +## Process application command line + +proc ::nns::ProcessCommandLine {} { + global argv + variable xcmd + variable xname + variable xdata + variable xpat * + variable xwatch 0 + + # Process the options, perform basic validation. + + if {[llength $argv] < 1} Usage + + set cmd [lindex $argv 0] + set argv [lrange $argv 1 end] + + switch -exact -- $cmd { + bind - ident - who - search {set xcmd $cmd} + default Usage + } + + while {[llength $argv]} { + set opt [lindex $argv 0] + if {![string match "-*" $opt]} break + + switch -exact -- $opt { + -host { + if {$xcmd == "who"} Usage + if {[llength $argv] < 2} Usage + + set host [lindex $argv 1] + set argv [lrange $argv 2 end] + + nameserv::auto::configure -host $host + } + -port { + if {$xcmd == "who"} Usage + if {[llength $argv] < 2} Usage + + # Todo: Check non-zero unsigned short integer + set port [lindex $argv 1] + set argv [lrange $argv 2 end] + + nameserv::auto::configure -port $port + } + -continuous { + set xwatch 1 + set argv [lrange $argv 1 end] + } + -debug { + # Undocumented. Activate the logger services provided + # by various packages. + logger::setlevel debug + set argv [lrange $argv 1 end] + } + default Usage + } + } + + # Additional validation, and extraction of the non-option + # arguments. Of which this application has none. + + switch -exact -- $xcmd { + bind { + if {[llength $argv] != 2} Usage + foreach {xname xdata} $argv break + } + search { + if {[llength $argv] > 1} Usage + if {[llength $argv] == 1} { + set xpat [lindex $argv 0] + } + } + who - ident { + if {[llength $argv] != 0} Usage + } + } + return +} + +proc ::nns::Usage {{sfx {}}} { + global argv0 ; append argv0 $sfx + set blank [blank $argv0] + puts stderr "$argv0 wrong#args, expected: bind ?-host NAME|IP? ?-port PORT? NAME DATA" + puts stderr "$blank ident ?-host NAME|IP? ?-port PORT?" + puts stderr "$blank search ?-host NAME|IP? ?-port PORT? ?-continuous? ?PATTERN?" + puts stderr "$blank who" + exit 1 +} + +proc ::nns::ArgError {text} { + global argv0 + puts stderr "$argv0: $text" + #puts $::errorInfo + exit 1 +} + +proc ::nns::blank {s} { + regsub -all -- {[^ ]} $s { } s + return $s +} + +# ### ### ### ######### ######### ######### + +proc ::nns::My {} { + # Quick access to format the identity of the name service the + # client talks to. + return "[nameserv::auto::cget -host] @[nameserv::auto::cget -port]" +} + +proc ::nns::Connection {message args} { + # args = tag event details, ignored + log::info $message + return +} + +proc ::nns::MonitorConnection {} { + uevent::bind nameserv lost-connection [list ::nns::Connection "Disconnected name service at [My]"] + uevent::bind nameserv re-connection [list ::nns::Connection "Reconnected2 name service at [My]"] + return +} + +# ### ### ### ######### ######### ######### +## Main + +proc ::nns::Do.bind {} { + global argv0 + variable xname + variable xdata + + MonitorConnection + log::info "Binding with name service at [My]: $xname = $xdata" + nameserv::auto::bind $xname $xdata + + vwait ::forever + # Not reached. + return +} + +proc ::nns::Do.ident {} { + set sp [nameserv::auto::server_protocol] + set sf [join [nameserv::auto::server_features] {, }] + + if {[llength $sf] > 1} { + set sf [linsert $sf end-1 and] + } + + puts "Server [My]" + puts " Protocol: $sp" + puts " Features: $sf" + return +} + +proc ::nns::Do.search {} { + variable xpat + variable xwatch + + struct::matrix M + M add columns 2 + + if {$xwatch} { + MonitorConnection + set contents [nameserv::auto::search -continuous $xpat] + $contents configure -command [list ::nns::Do.search.change $contents] + + vwait ::forever + # Not reached. + } else { + Do.search.print [nameserv::auto::search $xpat] + } + return +} + +proc ::nns::Do.search.change {res type response} { + # Ignoring the arguments, we simply print the full results every + # time. + + if {$type eq "stop"} { + # Cannot happen for nameserv::auto client, we are free to panic. + $res destroy + log::critical {Bad event 'stop' <=> Lost connection, search closed} + return + } + + # Clear screen ... + puts -nonewline stdout "\033\[H\033\[J"; # Home + Erase Down + flush stdout + + ::nns::Do.search.print [$res getall] + return +} + +proc ::nns::Do.search.print {contents} { + log::info "Searching at name service at [My]" + + if {![llength $contents]} { + log info "Nothing found..." + return + } + + catch {M delete rows [M rows]} + foreach {name data} $contents { + M add row [list $name $data] + } + + foreach line [split [M format 2string] \n] { log::info $line } + return +} + +proc ::nns::Do.who {} { + # FUTURE: access and print the metadata contained in ourselves. + global argv0 + puts "$argv0 [package require nns] (Client Protocol [nameserv::auto::protocol])" + return +} + +# ### ### ### ######### ######### ######### +## Invoking the functionality. + +::nns::ProcessCommandLine +if {[catch { + ::nns::Do.$::nns::xcmd +} msg]} { + ::nns::ArgError $msg +} + +# ### ### ### ######### ######### ######### +exit diff --git a/tcllib/apps/nns.man b/tcllib/apps/nns.man new file mode 100644 index 0000000..22363f1 --- /dev/null +++ b/tcllib/apps/nns.man @@ -0,0 +1,143 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin nns n 1.1] +[see_also nameserv(n)] +[see_also nameserv::common(n)] +[keywords application] +[keywords client] +[keywords {name service}] +[copyright {2007-2008 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Name service facility}] +[titledesc {Name service facility, Commandline Client Application}] +[category Networking] +[description] +[para] + +Please read [term {Name service facility, introduction}] first. + +[para] + +The application described by this document, [syscmd nns], is a simple +command line client for the nano name service facility provided by the +Tcllib packages [package nameserv], and [package nameserv::server]. + +Beyond that the application's sources also serve as an example of how +to use the client package [package nameserv]. All abilities of a +client are covered, from configuration to registration of names to +searching. + +[para] + +This name service facility has nothing to do with the Internet's +[term {Domain Name System}], otherwise known as [term DNS]. If the +reader is looking for a package dealing with that please see either of +the packages [package dns] and [package resolv], both found in Tcllib +too. + +[subsection {USE CASES}] + +[syscmd nns] was written with the following two main use cases in +mind. + +[para] +[list_begin enumerated] +[enum] +Registration of a name/data pair in the name service. + +[enum] +Searching the name service for entries matching a glob pattern. + +[list_end] + +[para] + +Beyond the above we also want to be able to identify the client, and +get information about the name service. + +[subsection {COMMAND LINE}] + +[list_begin definitions] + +[call [cmd nns] [method bind] \ + [opt "[option -host] [arg host]"] \ + [opt "[option -port] [arg port]"] \ + [arg name] [arg data]] + +This form registers the [arg name]/[arg data] pair in the specified +name service. In this form the command will [emph not] exit to keep +the registration alive. The user has to kill it explicitly, either by +sending a signal, or through the job-control facilities of the shell +in use. It will especially survive the loss of the connection to the +name service and reestablish the [arg name]/[arg data] pair when the +connection is restored. + +[para] +The options to specify the name service will be explained later, in +section [sectref OPTIONS]. + +[call [cmd nns] [method search] \ + [opt "[option -host] [arg host]"] \ + [opt "[option -port] [arg port]"] \ + [opt [option -continuous]] \ + [opt [arg pattern]]] + +This form searches the specified name service for entries matching the +glob-[arg pattern] and prints them to stdout, with each entry on its +own line. If no pattern is specified it defaults to [const *], +matching everything. + +[para] +The options to specify the name service will be explained later, in +section [sectref OPTIONS]. + +[para] + +If the option [option -continuous] is specified the client will not +exit after performing the search, but start to continuously monitor +the service for changes to the set of matching entries, appropriately +updating the display as changes arrive. In that form it will +especially also survive the loss of the connection to the name service +and reestablish the search when the connection is restored. + +[call [cmd nns] [method ident] \ + [opt "[option -host] [arg host]"] \ + [opt "[option -port] [arg port]"]] + +This form asks the specified name service for the version and features +of the name service protocol it supports and prints the results to +stdout. + +[para] +The options to specify the name service will be explained later, in +section [sectref OPTIONS]. + +[call [cmd nns] [method who]] + +This form prints name, version, and protocol version of the +application to stdout. + +[list_end] + +[subsection OPTIONS] + +This section describes all the options available to the user of the +application + +[para] +[list_begin options] +[opt_def -host name|ipaddress] + +If this option is not specified it defaults to [const localhost]. It +specifies the name or ip-address of the host the name service to talk +to is running on. + +[opt_def -port number] + +If this option is not specified it defaults to [const 38573]. It +specifies the TCP port the name service to talk to is listening on for +requests. + +[list_end] + +[vset CATEGORY nameserv] +[include ../modules/doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/apps/nnsd b/tcllib/apps/nnsd new file mode 100755 index 0000000..dd11233 --- /dev/null +++ b/tcllib/apps/nnsd @@ -0,0 +1,153 @@ +#! /usr/bin/env tclsh +# -*- tcl -*- + +# @@ Meta Begin +# Application nnsd 1.0.1 +# Meta platform tcl +# Meta summary Nano Name Service Demon +# Meta description This application is a simple demon on top +# Meta description of the nano name service facilities +# Meta subject {name service} server demon +# Meta require {Tcl 8.4} +# Meta require comm +# Meta require logger +# Meta require interp +# Meta require nameserv::common +# Meta require nameserv::server +# Meta author Andreas Kupries +# Meta license BSD +# @@ Meta End + +package provide nnsd 1.0.1 + +# nnsd - Nano Name Service Demon +# ==== = ======================= +# +# Use cases +# --------- +# +# (1) Run a simple trusted name service on some host. +# +# Command syntax +# -------------- +# +# Ad 1) nnsd ?-localonly BOOL? ?-port PORT? +# +# Run the server. If no port is specified the default port 38573 +# is used to listen for client. The option -localonly determines +# what connections are acceptable, local only (default), or +# remote connections as well. Local connections are whose +# originating from the same host which is running the server. +# Remote connections come from other hosts. + +lappend auto_path [file join [file dirname [file dirname [file normalize [info script]]]] modules] + +package require nameserv::server + +namespace eval ::nnsd {} + +proc ::nnsd::ProcessCommandLine {} { + global argv + + # Process the options, perform basic validation. + + while {[llength $argv]} { + set opt [lindex $argv 0] + if {![string match "-*" $opt]} break + + switch -exact -- $opt { + -localonly { + if {[llength $argv] % 2 == 1} Usage + + # Todo: Check boolean + set local [lindex $argv 1] + set argv [lrange $argv 2 end] + + nameserv::server::configure -localonly $local + } + -port { + if {[llength $argv] % 2 == 1} Usage + + # Todo: Check non-zero unsigned short integer + set port [lindex $argv 1] + set argv [lrange $argv 2 end] + + nameserv::server::configure -port $port + } + -debug { + # Undocumented. Activate the logger services provided + # by various packages. + logger::setlevel debug + set argv [lrange $argv 1 end] + } + default { + Usage + } + } + } + + # Additional validation, and extraction of the non-option + # arguments. Of which this application has none. + + if {[llength $argv]} Usage + + return +} + +proc ::nnsd::Usage {} { + global argv0 + puts stderr "$argv0 wrong#args, expected:\ + ?-localonly BOOL? ?-port PORT?" + exit 1 +} + +proc ::nnsd::ArgError {text} { + global argv0 + puts stderr "$argv0: $text" + exit 1 +} + +proc bgerror {args} { + puts stderr $args + puts stderr $::errorInfo + return +} + +# ### ### ### ######### ######### ######### +## Main + +proc ::nnsd::Headline {} { + global argv0 + set p [nameserv::server::cget -port] + set l [expr {[nameserv::server::cget -localonly] + ? "local only" + : "local & remote"}] + + puts "$argv0 [package require nnsd], listening on $p ($l)" + return +} + +proc ::nnsd::Do {} { + global argv0 + + ProcessCommandLine + + nameserv::server::start + Headline + + vwait forever + return +} + +# ### ### ### ######### ######### ######### +## Invoking the functionality. + +if {[catch { + ::nnsd::Do +} msg]} { + puts $::errorInfo + #::nnsd::ArgError $msg +} + +# ### ### ### ######### ######### ######### +exit diff --git a/tcllib/apps/nnsd.man b/tcllib/apps/nnsd.man new file mode 100644 index 0000000..28b139a --- /dev/null +++ b/tcllib/apps/nnsd.man @@ -0,0 +1,91 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin nnsd n 1.0.1] +[see_also nameserv::common(n)] +[see_also nameserv::server(n)] +[keywords application] +[keywords {name service}] +[keywords server] +[copyright {2007-2008 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Name service facility}] +[titledesc {Name service facility, Commandline Server Application}] +[category Networking] +[description] +[para] + +Please read [term {Name service facility, introduction}] first. + +[para] + +The application described by this document, [syscmd nns], is a simple +command line server for the nano name service facility provided by the +Tcllib packages [package nameserv], and [package nameserv::server]. + +Beyond that the application's sources also serve as an example of how +to use the server package [package nameserv::server]. + +[para] + +This name service facility has nothing to do with the Internet's +[term {Domain Name System}], otherwise known as [term DNS]. If the +reader is looking for a package dealing with that please see either of +the packages [package dns] and [package resolv], both found in Tcllib +too. + +[subsection {USE CASES}] + +[syscmd nnsd] was written with the following main use case in +mind. + +[para] +[list_begin enumerated] +[enum] +Run a nano name service on some host. + +[list_end] + +[para] + +[subsection {COMMAND LINE}] + +[list_begin definitions] + +[call [cmd nnsd] \ + [opt "[option -localonly] [arg flag]"] \ + [opt "[option -port] [arg port]"]] + +The command configures a server per the specified options and starts +it. The command will not exit on its own, as it keeps the name service +database wholly in memory. The user has to kill it explicitly, either +by sending a a signal, or through the job-control facilities of the +shell in use. + +[para] +The options to configure the name service are explained in section +[sectref OPTIONS]. + +[list_end] + +[subsection OPTIONS] + +This section describes all the options available to the user of the +application + +[para] +[list_begin options] +[opt_def -localonly bool] + +If this option is not specified it defaults to [const true], i.e. +acceptance of only local connections. The server will accept remote +connections, i.e. connections from other hosts, if and only if this +option is configured to [const false]. + +[opt_def -port number] + +If this option is not specified it defaults to [const 38573]. It +specifies the TCP port the server has to listen on for requests. + +[list_end] + +[vset CATEGORY nameserv] +[include ../modules/doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/apps/nnslog b/tcllib/apps/nnslog new file mode 100755 index 0000000..87989eb --- /dev/null +++ b/tcllib/apps/nnslog @@ -0,0 +1,182 @@ +#! /usr/bin/env tclsh +# -*- tcl -*- + +# @@ Meta Begin +# Application nnslog 1.1 +# Meta platform tcl +# Meta summary Nano Name Service Logger +# Meta description This application connects to a name service demon +# Meta description and then continuously logs all changes (new/removed +# Meta description definitions) to the standard output. It will survive +# Meta description the loss of the nameserver and automatically reconnect +# Meta description and continue when it comes back. +# Meta subject {name service} client log +# Meta require {Tcl 8.4} +# Meta require logger +# Meta require nameserv::auto +# Meta author Andreas Kupries +# Meta license BSD +# @@ Meta End + +package provide nnslog 1.0 + +# nns - Nano Name Service Logger +# === = ======================== +# +# Use cases +# --------- +# +# (1) Continuously monitor a nameservice for changes. +# +# Command syntax +# -------------- +# +# (Ad 1) nnslog ?-host NAME|IP? ?-port PORT? ?-color BOOL? +# +# Monitor a name server. If no port is specified the default +# port 38573 is used to connect to it. If no host is specified +# the default (localhost) is used to connect to it. + +# ### ### ### ######### ######### ######### +## Requirements + +lappend auto_path [file join [file dirname [file dirname \ + [file normalize [info script]]]] modules] + +package require nameserv::auto 0.3 ;# Need auto-restoring search. + +logger::initNamespace ::nnslog +namespace eval ::nnslog { log::setlevel info } + +# ### ### ### ######### ######### ######### +## Process application command line + +proc ::nnslog::ProcessCommandLine {} { + global argv + + # Process the options, perform basic validation. + set xcolor 0 + + if {[llength $argv] < 1} return + + while {[llength $argv]} { + set opt [lindex $argv 0] + if {![string match "-*" $opt]} break + + switch -exact -- $opt { + -host { + if {[llength $argv] < 2} Usage + + set host [lindex $argv 1] + set argv [lrange $argv 2 end] + + nameserv::configure -host $host + } + -port { + if {[llength $argv] < 2} Usage + + # Todo: Check non-zero unsigned short integer + set port [lindex $argv 1] + set argv [lrange $argv 2 end] + + nameserv::configure -port $port + } + -debug { + # Undocumented. Activate the logger services provided + # by various packages. + logger::setlevel debug + set argv [lrange $argv 1 end] + } + default Usage + } + } + + # Additional validation. no arguments should be left over. + if {[llength $argv] > 1} Usage + return +} + +proc ::nnslog::Usage {{sfx {}}} { + global argv0 ; append argv0 $sfx + puts stderr "$argv0 wrong#args, expected: ?-host NAME|IP? ?-port PORT?" + exit 1 +} + +proc ::nnslog::ArgError {text} { + global argv0 + puts stderr "$argv0: $text" + #puts $::errorInfo + exit 1 +} + +# ### ### ### ######### ######### ######### +## Setup a text|graphical report + +proc ::nnslog::My {} { + # Quick access to format the identity of the name service the + # client talks to. + return "[nameserv::auto::cget -host] @[nameserv::auto::cget -port]" +} + +proc ::nnslog::Connection {message args} { + # args = tag event details, ignored + log::info $message + return +} + +proc ::nnslog::MonitorConnection {} { + uevent::bind nameserv lost-connection [list ::nnslog::Connection "Disconnected name service at [My]"] + uevent::bind nameserv re-connection [list ::nnslog::Connection "Reconnected2 name service at [My]"] + return +} + +# ### ### ### ######### ######### ######### +## Main + +proc ::nnslog::Do.search {} { + MonitorConnection + set contents [nameserv::auto::search -continuous *] + $contents configure -command [list ::nnslog::Do.search.change $contents] + + log::info "Logging name service at [My]" + vwait ::forever + # Not reached. + return +} + +namespace eval ::nnslog { + variable map + array set map { + add +++ + remove --- + } +} + +proc ::nnslog::Do.search.change {res type response} { + variable map + + if {$type eq "stop"} { + # Cannot happen for nameserv::auto client, we are free to panic. + $res destroy + log::critical {Bad event 'stop' <=> Lost connection, search closed} + return + } + # Print events ... + foreach {name value} $response { + log::info "$map($type) : [list $name = $value]" + } + return +} + +# ### ### ### ######### ######### ######### +## Invoking the functionality. + +::nnslog::ProcessCommandLine +if {[catch { + ::nnslog::Do.search +} msg]} { + ::nnslog::ArgError $msg +} + +# ### ### ### ######### ######### ######### +exit diff --git a/tcllib/apps/nnslog.man b/tcllib/apps/nnslog.man new file mode 100644 index 0000000..91d93d1 --- /dev/null +++ b/tcllib/apps/nnslog.man @@ -0,0 +1,93 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin nnslog n 1.0] +[see_also nameserv(n)] +[see_also nameserv::common(n)] +[keywords application] +[keywords client] +[keywords {name service}] +[copyright {2008 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Name service facility}] +[titledesc {Name service facility, Commandline Logging Client Application}] +[category Networking] +[description] +[para] + +Please read [term {Name service facility, introduction}] first. + +[para] + +The application described by this document, [syscmd nnslog], is a +simple command line client for the nano name service facility provided +by the Tcllib packages [package nameserv], and [package nameserv::server]. + +[para] + +It essentially implements "[syscmd nns] search -continuous *", but +uses a different output formatting. Instead of continuously showing +the current contents of the server in the terminal it simply logs all +received add/remove events to [const stdout]. + +[para] + +This name service facility has nothing to do with the Internet's +[term {Domain Name System}], otherwise known as [term DNS]. If the +reader is looking for a package dealing with that please see either of +the packages [package dns] and [package resolv], both found in Tcllib +too. + +[subsection {USE CASES}] + +[syscmd nnslog] was written with the following main use case in mind. + +[para] +[list_begin enumerated] +[enum] +Monitoring the name service for all changes and logging them in a text +terminal. +[list_end] + +[para] + +[subsection {COMMAND LINE}] + +[list_begin definitions] +[call [cmd nnslog] \ + [opt "[option -host] [arg host]"] \ + [opt "[option -port] [arg port]"]] + +The command connects to the specified name service, sets up a search +for all changes and then prints all received events to stdout, with +each events on its own line. The command will not exit until it is +explicitly terminated by the user. It will especially survive the loss +of the connection to the name service and reestablish the search and +log when the connection is restored. + +[para] +The options to specify the name service will be explained later, in +section [sectref OPTIONS]. + +[list_end] + +[subsection OPTIONS] + +This section describes all the options available to the user of the +application + +[list_begin options] +[opt_def -host name|ipaddress] + +If this option is not specified it defaults to [const localhost]. It +specifies the name or ip-address of the host the name service to talk +to is running on. + +[opt_def -port number] + +If this option is not specified it defaults to [const 38573]. It +specifies the TCP port the name service to talk to is listening on for +requests. + +[list_end] + +[vset CATEGORY nameserv] +[include ../modules/doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/apps/page b/tcllib/apps/page new file mode 100755 index 0000000..e8985fa --- /dev/null +++ b/tcllib/apps/page @@ -0,0 +1,820 @@ +#! /usr/bin/env tclsh +# -*- tcl -*- + +# @@ Meta Begin +# Application page 1.0 +# Meta platform tcl +# Meta summary Tool for general text transformation +# Meta description While the name is an allusion to parser +# Meta description generation, the modular plugin-based +# Meta description nature of this application allows for +# Meta description any type of text transformation which +# Meta description can be put into a plugin. Still, the +# Meta description plugins coming with Tcllib all deal +# Meta description with parser generation. +# Meta category Processing text files +# Meta subject {parser generation} {text transformation} +# Meta require page::pluginmgr +# Meta require logger +# Meta require struct::matrix +# Meta author Andreas Kupries +# Meta license BSD +# @@ Meta End + +package provide page 1.0 + +lappend auto_path [file join [lindex $tcl_pkgPath end] page] +lappend auto_path [file join [file dirname [file dirname [file normalize [info script]]]] modules] + +#lappend auto_path [file join [file dirname [info script]] .. modules] +#source [file join [file dirname [info script]] .. modules struct tree.tcl] + +# /= +# $Id: page,v 1.3 2011/11/10 21:16:02 andreas_kupries Exp $ +# \= +# +# PAGE - PArser GEnerator | GTT - General Text Transformation +# ==== = ================ + === = =========================== +# +# Use cases +# --------- +# +# (1) Read a grammar specification and write out code implementing a +# parser for that grammar. +# +# (2) As (1), and additionally allow the user to select between a +# number of different backends for writing the results. +# Different forms for the same parser, pretty printing the +# grammar, different parser types (LL vs LR vs ...). Etc. +# +# (3) As (1) and/or (2), and additionally allow the user to select +# the frontend, i.e. the part reading the grammar. This allows +# the use of different input grammars for the specification of +# grammars, i.e. PEG, Yacc, Tyacc, Coco, etc. +# +# Note: For grammars it may be possible to write a unifying +# frontend whose reader grammar is able to recognize many +# different grammar formats without requiring the user to +# specify which format the supplied input is in. +# +# (4) As (1) and/or (2), and/or (3), and additionally allow the user +# to select the transformations to execute on the data provided +# by the frontend before it is given to the backend. At this +# point the parser generator has transformed into a general tool +# for the reading, transformation, and writing of any type of +# structured information. +# +# Note: For the use cases from (1) to (3) the representations returned +# by the frontend, and taken by the backend have to be fully +# specified to ensure that all the parts are working together. +# For the use case (4) it becomes the responsibility of the user +# of the tool to specify frontend, backed, and transformations +# which work properly together. + +# Command syntax +# -------------- +# +# Ad 1) page ?-rd peg|hb|ser? ?-gen tpcp|hb|ser|tree|peg|me|null? ?-min no|reach|use|all? [input|"-" [output|"-"]] +# +# The tool reads the grammar from the specified inputfile, +# transforms it as needed and then writes the resulting parser +# to the outputfile. Usage of "-" for the input signals that the +# grammar should be read from stdin. Analoguously usage of "-" +# for the output signals that the results should be written to +# stdout. +# +# Unspecified parts of the command line default to "-". +# +# Ad 2) Not specified yet. +# Ad 3) S.a. +# Ad 4) S.a. + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::pluginmgr ; # Management of the PAGE plugins. +package require logger ; # Logging subsystem for debugging. +package require struct::matrix ; # Matrices. For statistics report + +# ### ### ### ######### ######### ######### +## Internal data and status + +namespace eval ::page { + # Path to where the output goes to. The name of a file, or "-" for + # stdout. + + variable output "" + + # Path to where the input comes from. The name of a file, or "-" + # for stdin. + + variable input "" + + # Boolean flag. Input processing is timed. + + variable timed 0 + + # Boolean flag. Input processing has progressbar. + + variable progress 0 + + # Reader plugin and options. + + variable rd {} + + # List of transforms and their options. + + variable tr {} + + # Writer plugin an options. + + variable wr {} + + # ### ### ### ######### ######### ######### + + # Statistics. + # The number of characters read from the input. + + variable nread 0 + + # Progress + # Counter for when to print progress notification. + + variable ncount 0 + variable ndelta 100 + + # Collected statistical output. A matrix object, for proper + # columnar formatting when generating the report. And the last + # non-empty string in the first column, to prevent repetition. + + variable statistics {} + variable slast {} + + # ### ### ### ######### ######### ######### +} + +# ### ### ### ######### ######### ######### +## External data and status + +# This tool does not use external files to save and load status +# information. It has no history. If history is required, or data +# beyond the regular input see use cases (2-4). These may allow the +# specification of options specific to the selected frontend, backend, +# and transformations. + +# ### ### ### ######### ######### ######### +## Option processing. +## Validate command line. +## Full command line syntax. +## +# page [input|"-" [output|"-"]] +## + +proc ::page::ProcessCmdline {} { + global argv + + variable output + variable input + + set logging 0 + set n [ProcessArguments] + + # No options at all => Default -c peg. + + if {!$n} { + set argv [linsert $argv 0 -c peg] + ProcessArguments + } + + # Additional validation, and extraction of the non-option + # arguments. + + if {[llength $argv] > 2} Usage + + set input [lindex $argv 0] + set output [lindex $argv 1] + + # Final validation across the whole configuration. + + if {$input eq ""} { + set input - + } elseif {$input ne "-"} { + CheckInputFile $input {Input file} + } + + if {$output eq ""} { + set output - + } elseif {$output ne "-"} { + CheckTheOutput + } + + CheckReader + CheckWriter + CheckTransforms + + if {$logging} { + pluginmgr::log [::logger::init page] + } else { + pluginmgr::log {} + } + return +} + +proc ::page::ProcessArguments {} { + global argv + upvar 1 logging logging + + variable rd {} + variable tr {} + variable wr {} + variable timed 0 + variable progress 0 + + # Process the options, perform basic validation. + + set type {} + set name {} + set options {} + set mode {} + set nextmode {} + + set noptions 0 + + while {[llength $argv]} { + #puts ([join $argv ") ("]) + + set opt [lindex $argv 0] + if {![string match "-*" $opt]} { + # End of options reached. + break + } + incr noptions + Shift + switch -exact -- $opt { + --help - -h - -? {Usage} + --version - -V {Version} + + -v - --verbose - --log {set logging 1} + -q - --quiet - --nolog {set logging 0} + + -P {set progress 1} + -T {set timed 1} + + -D { + # Activate logging in the safe base for better debugging. + ::safe::setLogCmd {puts stderr} + } + + -r - -rd - --reader { + Complete + set type rd + set name [Shift] + set options {} + } + -w - -wr - --writer { + Complete + set type wr + set name [Shift] + set options {} + } + -t - -tr - --transform { + Complete + set type tr + set name [Shift] + if {$mode eq ""} {set mode tail} + set options {} + } + -c - --config { + set configfile [Shift] + if {($configfile eq "") || [catch { + set newargv [pluginmgr::configuration \ + $configfile] + } msg]} { + set msg [string map { + {Unable to locate} + {Unable to locate configuration}} $msg] + + ArgError "Bad argument \"$configfile\".\n\t$msg" + } + + if {[llength $newargv]} { + if {![llength $argv]} { + set argv $newargv + } else { + # linsert argv 0 {expanded}newargv + # -------------- + # linsert options 0 (linsert argv 0) + + set argv [eval [linsert $newargv 0 linsert $argv 0]] + #set argv [linsert $argv 0 {expand}$options] + } + } + } + -p - --prepend {set nextmode head} + -a - --append {set nextmode tail} + + --reset {Complete ; set tr {}} + + default { + # All unknown options go into the + # configuration of the last plugin + # defined (-r, -w, -t) + lappend options $opt [Shift] + } + } + } + + Complete + return $noptions +} + +proc ::page::Shift {} { + upvar 1 argv argv + if {![llength $argv]} {return {}} + set first [lindex $argv 0] + set argv [lrange $argv 1 end] + return $first +} + +proc ::page::Complete {} { + upvar 1 type type name name options options mode mode \ + nextmode nextmode rd rd wr wr tr tr + + #puts "$type $name ($options) \[$mode/$nextmode\]" + + set currentmode $mode + if {$nextmode ne $mode} { + set mode $nextmode + } + + if {$type eq ""} return + + switch -exact -- $type { + rd {set rd [list $name $options]} + wr {set wr [list $name $options]} + tr { + if {$currentmode eq "tail"} { + lappend tr [list $name $options] + } else { + set tr [linsert $tr 0 [list $name $options]] + } + } + } + return +} + +# ### ### ### ######### ######### ######### +## Option processing. +## Helpers: Generation of error messages. +## I. General usage/help message. +## II. Specific messages. +# +# Both write their messages to stderr and then +# exit the application with status 1. +## + +proc ::page::Usage {} { + global argv0 + puts stderr "Expected $argv0 ?options? ?inputpath|- ?outputpath|-??" + + puts stderr " --help, -h, -? This help" + puts stderr " --version, -V, Version information" + puts stderr " -v, --verbose, --log Activate logging in all loaded plugins" + puts stderr " -q, --quiet, --nolog Disable logging in all loaded plugins" + puts stderr " -P Activate progress feedback" + puts stderr " -T Activate collection of timings" + puts stderr " -r reader Specify input plugin" + puts stderr " -rd, --reader See above" + puts stderr " -w writer Specify output plugin" + puts stderr " -wr, --writer See above" + puts stderr " -t transform Specify processing plugin" + puts stderr " -tr, --transform See above" + puts stderr " -p, --prepend Place processing at front" + puts stderr " -a, --append Place processing at end" + puts stderr " --reset Clear list of transforms" + puts stderr " -c file Read configuration file" + puts stderr " --configuration See above." + puts stderr " " + + # --log, --nolog, -v, --verbose, -q, --quiet + + exit 1 +} + +proc ::page::Version {} { + puts stderr {$Id: page,v 1.3 2011/11/10 21:16:02 andreas_kupries Exp $} + exit 1 +} + +proc ::page::ArgError {text} { + global argv0 + puts stderr "$argv0: $text" + exit 1 +} + +proc in {list item} { + expr {([lsearch -exact $list $item] >= 0)} +} + +# ### ### ### ######### ######### ######### +## Check existence and permissions of an input/output file + +proc ::page::CheckReader {} { + variable rd + + if {![llength $rd]} { + ArgError "Input processing module is missing" + } + + foreach {name options} $rd break + + if {[catch { + set po [pluginmgr::reader $name] + } msg]} { + set msg [string map { + {Unable to locate} + {Unable to locate reader}} $msg] + + ArgError "Bad argument \"$name\".\n\t$msg" + } + + set opt {} + foreach {k v} $options { + if {![in $po $k]} { + ArgError "Input plugin $name: Bad option $k" + } + lappend opt $k $v + } + + pluginmgr::rconfigure $opt + return +} + +proc ::page::CheckWriter {} { + variable wr + + if {![llength $wr]} { + ArgError "Output module is missing" + } + + foreach {name options} $wr break + + if {[catch { + set po [pluginmgr::writer $name] + } msg]} { + set msg [string map { + {Unable to locate} + {Unable to locate writer}} $msg] + + ArgError "Bad argument \"$name\".\n\t$msg" + } + + set opt {} + foreach {k v} $options { + if {![in $po $k]} { + ArgError "Output plugin $name: Bad option $k" + } + lappend opt $k $v + } + + pluginmgr::wconfigure $opt + return +} + +proc ::page::CheckTransforms {} { + variable tr + + set idlist {} + foreach t $tr { + foreach {name options} $t break + + if {[catch { + foreach {id po} \ + [pluginmgr::transform $name] \ + break + } msg]} { + set msg [string map { + {Unable to locate} + {Unable to locate transformation}} $msg] + + ArgError "Bad argument \"$name\".\n\t$msg" + } + + set opt {} + foreach {k v} $options { + if {![in $po $k]} { + ArgError "Processing plugin $name: Bad option $k" + } + lappend opt $k $v + } + + pluginmgr::tconfigure $id $opt + lappend idlist $id + } + + set tr $idlist + return +} + +proc ::page::CheckInputFile {f label} { + if {![file exists $f]} { + ArgError "Unable to find $label \"$f\"" + } elseif {![file isfile $f]} { + ArgError "$label \"$f\" is not a file" + } elseif {![file readable $f]} { + ArgError "$label \"$f\" not readable (permission denied)" + } + return +} + +proc ::page::CheckTheOutput {} { + variable output + + set base [file dirname $output] + if {$base eq ""} {set base [pwd]} + + if {![file exists $output]} { + if {![file exists $base]} { + ArgError "Output base path \"$base\" not found" + } + if {![file writable $base]} { + ArgError "Output base path \"$base\" not writable (permission denied)" + } + } elseif {![file writable $output]} { + ArgError "Output path \"$output\" not writable (permission denied)" + } elseif {![file isfile $output]} { + ArgError "Output path \"$output\" is not a file" + } + + return +} + +# ### ### ### ######### ######### ######### +## Commands implementing the main functionality. + +proc ::page::Read {} { + variable input + variable progress + variable timed + variable nread + + set label \[[pluginmgr::rlabel]\] + set msg "" + append msg $label " " + + if {$input eq "-"} { + append msg {Reading grammar from stdin} + set chan stdin + } else { + append msg {Reading grammar from file "} $input {"} + set chan [open $input r] + } + + pluginmgr::report info $msg + + if {!$timed && !$progress} { + # Regular run + set data [pluginmgr::read \ + [list read $chan] [list eof $chan]] + + } elseif {$timed && $progress} { + # Timed, with feedback + if {[pluginmgr::rtimeable]} { + pluginmgr::rtime + set data [pluginmgr::read \ + [list ::page::ReadPT $chan] [list eof $chan] \ + ::page::ReadComplete] + set usec [pluginmgr::rgettime] + } else { + set usec [lindex [time { + set data [pluginmgr::read \ + [list ::page::ReadPT $chan] [list eof $chan] \ + ::page::ReadComplete] + }] 0] ; # {} + } + } elseif {$timed} { + # Timed only + if {[pluginmgr::rtimeable]} { + pluginmgr::rtime + set data [pluginmgr::read \ + [list ::page::ReadT $chan] [list eof $chan]] + set usec [pluginmgr::rgettime] + } else { + set usec [lindex [time { + set data [pluginmgr::read \ + [list ::page::ReadT $chan] [list eof $chan]] + }] 0] ; # {} + } + } else { + # Feedback only ... + set data [pluginmgr::read \ + [list ::page::ReadPT $chan] [list eof $chan] \ + ::page::ReadComplete] + } + + if {$input ne "-"} { + close $chan + } + + if {$timed} { + Statistics $label "Characters:" $nread + Statistics $label "Seconds:" [expr {double($usec)/1000000}] + Statistics $label "Char/Seconds:" [expr {1000000*double($nread)/$usec}] + Statistics $label "Microseconds:" $usec + Statistics $label "Microsec/Char:" [expr {$usec/double($nread)}] + } elseif {$progress} { + pluginmgr::report info " Read $nread [expr {$nread == 1 ? "character" : "characters"}]" + } + return $data +} + +proc ::page::Transform {data} { + variable timed + variable tr + + if {$data eq ""} {return $data} + + if 0 { + pluginmgr::report info ---------------------------- + foreach tid $tr { + set label "\[[pluginmgr::tlabel $tid]\]" + pluginmgr::report info $label + } + pluginmgr::report info ---------------------------- + } + + #puts /($data)/ + + foreach tid $tr { + set label "\[[pluginmgr::tlabel $tid]\]" + + pluginmgr::report info $label + + if {!$timed} { + set data [pluginmgr::transform_do $tid $data] + } else { + if {[pluginmgr::ttimeable $tid]} { + pluginmgr::ttime $tid + set data [pluginmgr::transform_do $tid $data] + set usec [pluginmgr::tgettime $tid] + } else { + set usec [lindex [time { + set data [pluginmgr::transform_do $tid $data] + }] 0]; #{} + } + Statistics $label Seconds: [expr {double($usec)/1000000}] + } + } + return $data +} + +proc ::page::Write {data} { + variable timed + variable output + + if {$data eq ""} {return $data} + + set label \[[pluginmgr::wlabel]\] + set msg "" + append msg $label " " + + if {$output eq "-"} { + append msg {Writing to stdout} + set chan stdout + } else { + append msg {Writing to file "} $output {"} + set chan [open $output w] + } + + pluginmgr::report info $msg + + if {!$timed} { + pluginmgr::write $chan $data + } else { + if {[pluginmgr::wtimeable]} { + pluginmgr::wtime + pluginmgr::write $chan $data + set usec [pluginmgr::wgettime] + } else { + set usec [lindex [time { + pluginmgr::write $chan $data + }] 0]; #{} + } + Statistics $label Seconds: [expr {double($usec)/1000000}] + } + + if {$output ne "-"} { + close $chan + } + return +} + +proc ::page::StatisticsBegin {} { + variable timed + variable statistics + if {!$timed} return + + set statistics [struct::matrix ::page::STAT] + + Statistics _Statistics_________ + return +} + +proc ::page::Statistics {module args} { + variable statistics + variable slast + + set n [expr {1+[llength $args]}] + + if {[$statistics columns] < $n} { + $statistics add columns [expr { + $n - [$statistics columns] + }] ; # {} + } + + if {$module eq $slast} { + set prefix "" + } else { + set prefix $module + set slast $module + } + + $statistics add row [linsert $args 0 $prefix] + return +} + +proc ::page::StatisticsComplete {} { + variable timed + variable statistics + if {!$timed} return + + pluginmgr::report info "" + foreach line [split [$statistics \ + format 2string] \n] { + pluginmgr::report info $line + } + return +} + +# ### ### ### ######### ######### ######### +## Helper commands. + +proc ::page::ReadPT {chan {n {}}} { + variable nread + variable ncount + variable ndelta + + if {$n eq ""} { + set data [read $chan] + } else { + set data [read $chan $n] + } + + set n [string length $data] + incr nread $n + + while {$ncount < $nread} { + puts -nonewline stderr . + flush stderr + incr ncount $ndelta + } + + return $data +} + +proc ::page::ReadComplete {} { + puts stderr "" + flush stderr + return +} + +proc ::page::ReadT {chan {n {}}} { + variable nread + + if {$n eq ""} { + set data [read $chan] + } else { + set data [read $chan $n] + } + + set n [string length $data] + incr nread $n + + return $data +} + +# ### ### ### ######### ######### ######### +## Invoking the functionality. + +if {[catch { + ::page::ProcessCmdline + ::page::StatisticsBegin + ::page::Write [::page::Transform [::page::Read]] + ::page::StatisticsComplete +} msg]} { + puts $::errorInfo + #::page::ArgError $msg +} + +# ### ### ### ######### ######### ######### +exit diff --git a/tcllib/apps/page.man b/tcllib/apps/page.man new file mode 100644 index 0000000..bae424d --- /dev/null +++ b/tcllib/apps/page.man @@ -0,0 +1,467 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin page n 1.0] +[see_also page::pluginmgr] +[keywords {parser generator}] +[keywords {text processing}] +[copyright {2005 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[titledesc {Parser Generator}] +[moddesc {Development Tools}] +[category {Page Parser Generator}] +[description] +[para] + +The application described by this document, [syscmd page], is actually +not just a parser generator, as the name implies, but a generic tool +for the execution of arbitrary transformations on texts. + +[para] + +Its genericity comes through the use of [term plugins] for reading, +transforming, and writing data, and the predefined set of plugins +provided by Tcllib is for the generation of memoizing recursive +descent parsers (aka [term {packrat parsers}]) from grammar +specifications ([term {Parsing Expression Grammars}]). + +[para] + +[syscmd page] is written on top of the package + +[package page::pluginmgr], wrapping its functionality into a command +line based application. All the other [package page::*] packages are +plugin and/or supporting packages for the generation of parsers. The +parsers themselves are based on the packages [package grammar::peg], +[package grammar::peg::interp], and [package grammar::mengine]. + +[subsection {COMMAND LINE}] + +[list_begin definitions] + +[call [cmd page] [opt [arg options]...] [opt "[arg input] [opt [arg output]]"]] + +This is general form for calling [syscmd page]. The application will +read the contents of the file [arg input], process them under the +control of the specified [arg options], and then write the result to +the file [arg output]. + +[para] + +If [arg input] is the string [const -] the data to process will be +read from [const stdin] instead of a file. Analogously the result will +be written to [const stdout] instead of a file if [arg output] is the +string [const -]. A missing output or input specification causes the +application to assume [const -]. + +[para] + +The detailed specifications of the recognized [arg options] are +provided in section [sectref OPTIONS]. + +[list_begin arguments] +[arg_def path input in] + +This argument specifies the path to the file to be processed by the +application, or [const -]. The last value causes the application to +read the text from [const stdin]. Otherwise it has to exist, and be +readable. If the argument is missing [const -] is assumed. + +[arg_def path output in] + +This argument specifies where to write the generated text. It can be +the path to a file, or [const -]. The last value causes the +application to write the generated documented to [const stdout]. + +[para] + +If the file [arg output] does not exist then +[lb]file dirname $output[rb] has to exist and must be a writable +directory, as the application will create the fileto write to. + +[para] + +If the argument is missing [const -] is assumed. + +[list_end] +[list_end] + +[subsection OPERATION] + +... reading ... transforming ... writing - plugins - pipeline ... + +[subsection OPTIONS] + +This section describes all the options available to the user of the +application. Options are always processed in order. I.e. of both +[option --help] and [option --version] are specified the option +encountered first has precedence. + +[para] + +Unknown options specified before any of the options [option -rd], +[option -wr], or [option -tr] will cause processing to abort with an +error. Unknown options coming in between these options, or after the +last of them are assumed to always take a single argument and are +associated with the last plugin option coming before them. They will +be checked after all the relevant plugins, and thus the options they +understand, are known. I.e. such unknown options cause error if and +only if the plugin option they are associated with does not understand +them, and was not superceded by a plugin option coming after. + +[para] + +Default options are used if and only if the command line did not +contain any options at all. They will set the application up as a +PEG-based parser generator. The exact list of options is + +[para] +[example {-c peg}] +[para] + +And now the recognized options and their arguments, if they have any: + +[para] +[list_begin options] + +[opt_def --help] +[opt_def -h] +[opt_def -?] + +When one of these options is found on the command line all arguments +coming before or after are ignored. The application will print a short +description of the recognized options and exit. + +[opt_def --version] +[opt_def -V] + +When one of these options is found on the command line all arguments +coming before or after are ignored. The application will print its +own revision and exit. + +[opt_def -P] + +This option signals the application to activate visual feedback while +reading the input. + +[opt_def -T] + +This option signals the application to collect statistics while +reading the input and to print them after reading has completed, +before processing started. + +[opt_def -D] + +This option signals the application to activate logging in the Safe +base, for the debugging of problems with plugins. + +[opt_def -r parser] +[opt_def -rd parser] +[opt_def --reader parser] + +These options specify the plugin the application has to use for +reading the [arg input]. If the options are used multiple times the +last one will be used. + +[opt_def -w generator] +[opt_def -wr generator] +[opt_def --writer generator] + +These options specify the plugin the application has to use for +generating and writing the final [arg output]. If the options are used +multiple times the last one will be used. + +[opt_def -t process] +[opt_def -tr process] +[opt_def --transform process] + +These options specify a plugin to run on the input. In contrast to +readers and writers each use will [emph not] supersede previous +uses, but add each chosen plugin to a list of transformations, either +at the front, or the end, per the last seen use of either option +[option -p] or [option -a]. The initial default is to append the new +transformations. + +[opt_def -a] +[opt_def --append] + +These options signal the application that all following +transformations should be added at the end of the list of +transformations. + +[opt_def -p] +[opt_def --prepend] + +These options signal the application that all following +transformations should be added at the beginning of the list of +transformations. + +[opt_def --reset] + +This option signals the application to clear the list of +transformations. This is necessary to wipe out the default +transformations used. + +[opt_def -c file] +[opt_def --configuration file] + +This option causes the application to load a configuration file and/or +plugin. This is a plugin which in essence provides a pre-defined set +of commandline options. They are processed exactly as if they have +been specified in place of the option and its arguments. This means +that unknown options found at the beginning of the configuration file +are associated with the last plugin, even if that plugin was specified +before the configuration file itself. Conversely, unknown options +coming after the configuration file can be associated with a plugin +specified in the file. + +[para] + +If the argument is a file which cannot be loaded as a plugin the +application will assume that its contents are a list of options and +their arguments, separated by space, tabs, and newlines. Options and +argumentes containing spaces can be quoted via double-quotes (") and +quotes ('). The quote character can be specified within in a quoted +string by doubling it. Newlines in a quoted string are accepted as is. + +[comment {"}] +[list_end] + +[subsection PLUGINS] + +[syscmd page] makes use of four different types of plugins, namely: +readers, writers, transformations, and configurations. Here we provide +only a basic introduction on how to use them from [syscmd page]. The +exact APIs provided to and expected from the plugins can be found in +the documentation for [package page::pluginmgr], for those who wish to +write their own plugins. + +[para] + +Plugins are specified as arguments to the options [option -r], +[option -w], [option -t], [option -c], and their equivalent longer +forms. See the section [sectref OPTIONS] for reference. + +[para] + +Each such argument will be first treated as the name of a file and +this file is loaded as the plugin. If however there is no file with +that name, then it will be translated into the name of a package, and +this package is then loaded. For each type of plugins the package +management searches not only the regular paths, but a set application- +and type-specific paths as well. Please see the section +[sectref {PLUGIN LOCATIONS}] for a listing of all paths and their +sources. + +[para] + +[list_begin definitions] +[def "[option -c] [arg name]"] + +Configurations. The name of the package for the plugin [arg name] is +"page::config::[arg name]". + +[para] +We have one predefined plugin: + +[list_begin definitions] +[def [emph peg]] + +It sets the application up as a parser generator accepting parsing +expression grammars and writing a packrat parser in Tcl. The actual +arguments it specifies are: + +[para] +[example { + --reset + --append + --reader peg + --transform reach + --transform use + --writer me +}] +[para] + +[list_end] + +[def "[option -r] [arg name]"] + +Readers. The name of the package for the plugin [arg name] is +"page::reader::[arg name]". + +[para] +We have five predefined plugins: + +[list_begin definitions] +[def [emph peg]] + +Interprets the input as a parsing expression grammar ([term PEG]) and +generates a tree representation for it. Both the syntax of PEGs and +the structure of the tree representation are explained in their own +manpages. + +[def [emph hb]] + +Interprets the input as Tcl code as generated by the writer plugin +[emph hb] and generates its tree representation. + +[def [emph ser]] + +Interprets the input as the serialization of a PEG, as generated by +the writer plugin [emph ser], using the package +[package grammar::peg]. + +[def [emph lemon]] + +Interprets the input as a grammar specification as understood by +Richard Hipp's [term LEMON] parser generator and generates a tree +representation for it. Both the input syntax and the structure of the +tree representation are explained in their own manpages. + +[def [emph treeser]] + +Interprets the input as the serialization of a +[package struct::tree]. It is validated as such, +but nothing else. It is [emph not] assumed to +be the tree representation of a grammar. +[list_end] + +[def "[option -w] [arg name]"] + +Writers. The name of the package for the plugin [arg name] is +"page::writer::[arg name]". + +[para] +We have eight predefined plugins: + +[list_begin definitions] + +[def [emph identity]] + +Simply writes the incoming data as it is, without making any +changes. This is good for inspecting the raw result of a reader or +transformation. + +[def [emph null]] + +Generates nothing, and ignores the incoming data structure. + +[def [emph tree]] + +Assumes that the incoming data structure is a [package struct::tree] +and generates an indented textual representation of all nodes, their +parental relationships, and their attribute information. + +[def [emph peg]] + +Assumes that the incoming data structure is a tree representation of a +[term PEG] or other other grammar and writes it out as a PEG. The +result is nicely formatted and partially simplified (strings as +sequences of characters). A pretty printer in essence, but can also be +used to obtain a canonical representation of the input grammar. + +[def [emph tpc]] + +Assumes that the incoming data structure is a tree representation of a +[term PEG] or other other grammar and writes out Tcl code defining a +package which defines a [package grammar::peg] object containing the +grammar when it is loaded into an interpreter. + +[def [emph hb]] + +This is like the writer plugin [emph tpc], but it writes only the +statements which define stat expression and grammar rules. The code +making the result a package is left out. + +[def [emph ser]] + +Assumes that the incoming data structure is a tree representation of a +[term PEG] or other other grammar, transforms it internally into a +[package grammar::peg] object and writes out its serialization. + +[def [emph me]] + +Assumes that the incoming data structure is a tree representation of a +[term PEG] or other other grammar and writes out Tcl code defining a +package which implements a memoizing recursive descent parser based on +the match engine (ME) provided by the package [package grammar::mengine]. + +[list_end] + +[def "[option -t] [arg name]"] + +Transformers. The name of the package for the plugin [arg name] is +"page::transform::[arg name]". + +[para] +We have two predefined plugins: + +[list_begin definitions] +[def [emph reach]] + +Assumes that the incoming data structure is a tree representation of a +[term PEG] or other other grammar. It determines which nonterminal +symbols and rules are reachable from start-symbol/expression. All +nonterminal symbols which were not reached are removed. + +[def [emph use]] + +Assumes that the incoming data structure is a tree representation of a +[term PEG] or other other grammar. It determines which nonterminal +symbols and rules are able to generate a [emph finite] sequences of +terminal symbols (in the sense for a Context Free Grammar). All +nonterminal symbols which were not deemed useful in this sense are +removed. + +[list_end] +[list_end] + +[subsection {PLUGIN LOCATIONS}] + +The application-specific paths searched by [syscmd page] either are, +or come from: + +[para] + +[list_begin enumerated] +[enum] The directory [file ~/.page/plugin] +[enum] The environment variable [term PAGE_PLUGINS] +[enum] The registry entry [term "HKEY_LOCAL_MACHINE\\SOFTWARE\\PAGE\\PLUGINS"] +[enum] The registry entry [term "HKEY_CURRENT_USER\\SOFTWARE\\PAGE\\PLUGINS"] +[list_end] + +[para] + +The type-specific paths searched by [syscmd page] either are, or come +from: + +[para] +[list_begin enumerated] +[enum] The directory [file ~/.page/plugin/<TYPE>] +[enum] The environment variable [term PAGE_<TYPE>_PLUGINS] +[enum] The registry entry [term "HKEY_LOCAL_MACHINE\\SOFTWARE\\PAGE\\<TYPE>\\PLUGINS"] +[enum] The registry entry [term "HKEY_CURRENT_USER\\SOFTWARE\\PAGE\\<TYPE>\\PLUGINS"] +[list_end] + +[para] + +Where the placeholder [term <TYPE>] is always one of the values below, +properly capitalized. + +[list_begin enumerated] +[enum] reader +[enum] writer +[enum] transform +[enum] config +[list_end] +[para] + +The registry entries are specific to the Windows(tm) platform, all +other platforms will ignore them. + +[para] + +The contents of both environment variables and registry entries are +interpreted as a list of paths, with the elements separated by either +colon (Unix), or semicolon (Windows). + +[vset CATEGORY page] +[include ../modules/doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/apps/pt b/tcllib/apps/pt new file mode 100755 index 0000000..7389604 --- /dev/null +++ b/tcllib/apps/pt @@ -0,0 +1,156 @@ +#!/usr/bin/env tclsh +# -*- tcl -*- + +package require Tcl 8.5 +# activate commands below for execution from within the pt directory +set self [file normalize [info script]] +set selfdir [file dirname $self] +lappend auto_path $selfdir [file dirname $selfdir] +# When debugging package loading trouble, show the search paths +#puts [join $auto_path \n] + +# # ## ### ##### ######## ############# ##################### + +package require pt::pgen 1.0.3 +package require pt::util +package require fileutil +package require try + +namespace eval ::pt::app { + namespace export generate help + namespace ensemble create +} + +# # ## ### ##### ######## ############# ##################### + +proc main {} { + global argv argv0 errorInfo + if {![llength $argv]} { lappend argv help } + if {[catch { + set status [::pt::app {*}$argv] + } msg]} { + set elines [split $errorInfo \n] + if {[llength $elines] == 3} { + if {[string match *unknown* $msg]} { + #puts stderr "$argv0 $msg" + ::pt::app help + exit 1 + } elseif {[string match {*wrong # args*} $msg]} { + #puts $msg + # Extracting the command name from the error message, + # because there a prefix will have been expanded to + # the actual command. <lindex argv 0> OTOH would be a + # possible prefix, without a properly matching topic. + puts stderr Usage: + ::pt::app help [lindex $msg 5 1] + exit 1 + } + } + set prefix {INTERNAL ERROR :: } + puts ${prefix}[join $elines \n$prefix] + exit 1 + } + exit $status +} + +# # ## ### ##### ######## ############# ##################### + +proc ::pt::app::helpHelp {} { + return { + @ help ?TOPIC? + + Provides general help, or specific to the given topic. + } +} +proc ::pt::app::help {{topic {}}} { + global argv0 + if {[llength [info level 0]] == 1} { + puts stderr "Usage: $argv0 command ...\n\nKnown commands:\n" + foreach topic [Topics] { + ::pt::app help $topic + } + } elseif {$topic ni [Topics]} { + puts stderr "$argv0: Unknown help topic '$topic'" + puts stderr "\tUse one of [linsert [join [Topics] {, }] end-1 or]" + puts stderr "" + } else { + puts stderr \t[join [split [string map [list @ $argv0] [string trim [::pt::app::${topic}Help]]] \n] \n\t] + puts stderr "" + } + return 0 +} + +proc ::pt::app::Topics {} { + namespace eval ::TEMP { namespace import ::pt::app::* } + set commands [info commands ::TEMP::*] + namespace delete ::TEMP + + set res {} + foreach c $commands { + lappend res [regsub ^::TEMP:: $c {}] + } + proc ::pt::app::Topics {} [list return $res] + return $res +} + +# # ## ### ##### ######## ############# ##################### + +proc ::pt::app::generateHelp {} { + return { + @ generate PFORMAT ?-option value...? PFILE INFORMAT GFILE + + Generate data in format PFORMAT and write it to PFILE. Read + the grammar to be processed from GFILE (assuming the format + GFORMAT). Use any options to configure the generator. The are + dependent on PFORMAT. + } +} +proc ::pt::app::generate {args} { + # args = parserformat ?...? parserfile grammarformat grammarfile + + if {[llength $args] < 4} { + # Just enough that the help code can extract the method name + return -code error "wrong # args, should be \"@ generate ...\"" + } + + set args [lassign $args parserformat] + lassign [lrange $args end-2 end] \ + parserfile grammarformat grammarfile + set args [Template [lrange $args 0 end-3]] + lappend args -file $grammarfile + + puts "Reading $grammarformat $grammarfile ..." + set grammar [fileutil::cat $grammarfile] + + puts "Generating a $parserformat parser ..." + try { + set parser [::pt::pgen $grammarformat $grammar $parserformat {*}$args] + } trap {PT RDE SYNTAX} {e o} { + puts [pt::util error2readable $e $grammar] + return 1 + } + + puts "Saving to $parserfile ..." + fileutil::writeFile $parserfile $parser + + puts OK + return 0 +} + +# Lift template specifications from file paths to the file's contents. + +proc ::pt::app::Template {optiondict} { + set res {} + foreach {option value} $optiondict { + if {$option eq "-template"} { + set value [fileutil::cat $value] + } + lappend res $option $value + } + return $res +} + +# # ## ### ##### ######## ############# ##################### + +main +exit diff --git a/tcllib/apps/pt.man b/tcllib/apps/pt.man new file mode 100644 index 0000000..577b625 --- /dev/null +++ b/tcllib/apps/pt.man @@ -0,0 +1,242 @@ +[comment {-*- text -*- doctools manpage}] +[manpage_begin pt n 1] +[include ../modules/pt/include/module.inc] +[titledesc {Parser Tools Application}] +[description] +[include ../modules/pt/include/ref_intro.inc] + +This document describes [cmd pt], the main application of the module, +a [term {parser generator}]. Its intended audience are people who wish +to create a parser for some language of theirs. Should you wish to +modify the application instead, please see the section about the +application's [sectref {Internals}] for the basic references. + +[para] + +It resides in the User Application Layer of Parser Tools. +[para][image arch_user_app][para] + +[section {Command Line}] + +[list_begin definitions] + +[call [cmd pt] [method generate] \ + [arg resultformat] [opt [arg options...]] [arg resultfile] \ + [arg inputformat] [arg inputfile]] + +This sub-command of the application reads the parsing expression +grammar stored in the [arg inputfile] in the format [arg inputformat], +converts it to the [arg resultformat] under the direction of the +(format-specific) set of options specified by the user and stores the +result in the [arg resultfile]. + +[para] + +The [arg inputfile] has to exist, while the [arg resultfile] may be +created, overwriting any pre-existing content of the file. Any missing +directory in the path to the [arg resultfile] will be created as well. + +[para] + +The exact form of the result for, and the set of options supported by +the known result-formats, are explained in the upcoming sections of +this document, with the list below providing an index mapping between +format name and its associated section. In alphabetical order: + +[para] +[list_begin definitions] +[def [const c]] A [term resultformat]. See section [sectref {C Parser}]. +[def [const container]] A [term resultformat]. See section [sectref {Grammar Container}]. +[def [const critcl]] A [term resultformat]. See section [sectref {C Parser Embedded In Tcl}]. +[def [const json]] A [term input]- and [term resultformat]. See section [sectref {JSON Grammar Exchange}]. +[def [const oo]] A [term resultformat]. See section [sectref {TclOO Parser}]. +[def [const peg]] A [term input]- and [term resultformat]. See section [sectref {PEG Specification Language}]. +[def [const snit]] A [term resultformat]. See section [sectref {Snit Parser}]. +[list_end] +[list_end] + +Of the seven possible results four are parsers outright ([const c], +[const critcl], [const oo], and [const snit]), one ([const container]) +provides code which can be used in conjunction with a generic parser +(also known as a grammar interpreter), and the last two ([const json] +and [const peg]) are doing double-duty as input formats, allowing the +transformation of grammars for exchange, reformatting, and the like. + +[para] + +The created parsers fall into three categories: +[include ../modules/pt/include/gen_options.inc] + +[list_begin definitions] + +[def [const {Specialized parsers implemented in C}]] + +The fastest parsers are created when using the result formats +[const c] and [const critcl]. The first returns the raw C code for the +parser, while the latter wraps it into a Tcl package using +[term CriTcl]. + +[para] + +This makes the latter much easier to use than the former. On the other +hand, the former can be adapted to the users' requirements through a +multitude of options, allowing for things like usage of the parser +outside of a Tcl environment, something the [const critcl] format +doesn't support. As such the [const c] format is meant for more +advanced users, or users with special needs. + +[para] + +A disadvantage of all the parsers in this section is the need to run +them through a C compiler to make them actually executable. This is +not something everyone has the necessary tools for. The parsers in the +next section are for people under such restrictions. + +[def [const {Specialized parsers implemented in Tcl}]] + +As the parsers in this section are implemented in Tcl they are quite a +bit slower than anything from the previous section. On the other hand +this allows them to be used in pure-Tcl environments, or in +environments which allow only a limited set of binary packages. In the +latter case it will be advantageous to lobby for the inclusion of the +C-based runtime support (notes below) into the environment to reduce +the impact of Tcl's on the speed of these parsers. + +[para] + +The relevant formats are [const snit] and [const oo]. Both place their +result into a Tcl package containing a [cmd snit::type], or TclOO +[cmd class] respectively. + +[para] + +Of the supporting runtime, which is the package [package pt::rde], the +user has to know nothing but that it does exist and that the parsers +are dependent on it. Knowledge of the API exported by the runtime for +the parsers' consumption is [emph not] required by the parsers' users. + +[def [const {Interpreted parsing implemented in Tcl}]] + +The last category, grammar interpretation. This means that an +interpreter for parsing expression grammars takes the description of +the grammar to parse input for, and uses it guide the parsing process. + +This is the slowest of the available options, as the interpreter has +to continually run through the configured grammar, whereas the +specialized parsers of the previous sections have the relevant +knowledge about the grammar baked into them. + +[para] + +The only places where using interpretation make sense is where the +grammar for some input may be changed interactively by the user, as +the interpretation allows for quick turnaround after each change, +whereas the previous methods require the generation of a whole new +parser, which is not as fast. + +On the other hand, wherever the grammar to use is fixed, the previous +methods are much more advantageous as the time to generate the parser +is minuscule compared to the time the parser code is in use. + +[para] + +The relevant result format is [const container]. + +It (quickly) generates grammar descriptions (instead of a full parser) +which match the API expected by ParserTools' grammar interpreter. + +The latter is provided by the package [package pt::peg::interp]. + +[list_end] + +All the parsers generated by [const critcl], [const snit], and +[const oo], and the grammar interpreter share a common API for access +to the actual parsing functionality, making them all +plug-compatible. + +It is described in the [manpage {Parser API}] specification document. + +[section {PEG Specification Language}] +[include ../modules/pt/include/format/whatis_peg.inc] +[para] + +For either an introduction to or the formal specification of the +language, please go and read the [manpage {PEG Language Tutorial}]. + +[para] + +When used as a result-format this format supports the following +options: + +[include ../modules/pt/include/format/options_peg.inc] + +[section {JSON Grammar Exchange}] +[include ../modules/pt/include/format/whatis_json.inc] +[para] + +For the formal specification of the JSON grammar exchange format, +please go and read [manpage {The JSON Grammar Exchange Format}]. + +[para] + +When used as a result-format this format supports the following +options: + +[include ../modules/pt/include/format/options_json.inc] + +[section {C Parser Embedded In Tcl}] +[include ../modules/pt/include/format/whatis_cparam_critcl.inc] +[para] + +This result-format supports the following options: + +[include ../modules/pt/include/format/options_cparam_critcl.inc] + +[section {C Parser}] +[include ../modules/pt/include/format/whatis_cparam_rawc.inc] +[para] + +This result-format supports the following options: + +[include ../modules/pt/include/format/options_cparam_rawc.inc] + +[section {Snit Parser}] +[include ../modules/pt/include/format/whatis_tclparam_snit.inc] +[para] + +This result-format supports the following options: + +[include ../modules/pt/include/format/options_tclparam_snit.inc] + +[section {TclOO Parser}] +[include ../modules/pt/include/format/whatis_tclparam_oo.inc] +[para] + +This result-format supports the following options: + +[include ../modules/pt/include/format/options_tclparam_oo.inc] + +[section {Grammar Container}] +[include ../modules/pt/include/format/whatis_container.inc] +[para] + +This result-format supports the following options: + +[include ../modules/pt/include/format/options_container.inc] + +[section Example] +[vset MODE app][include ../modules/pt/include/example/full.inc] + +[section Internals] + +This section is intended for users of the application which wish to +modify or extend it. Users only interested in the generation of +parsers can ignore it. + +[para] + +The main functionality of the application is encapsulated in the +package [package pt::pgen]. Please read it for more information. + +[include ../modules/pt/include/feedback.inc] +[manpage_end] diff --git a/tcllib/apps/tcldocstrip b/tcllib/apps/tcldocstrip new file mode 100755 index 0000000..6d73425 --- /dev/null +++ b/tcllib/apps/tcldocstrip @@ -0,0 +1,537 @@ +#! /usr/bin/env tclsh +# -*- tcl -*- + +# @@ Meta Begin +# Application tcldocstrip 1.0.1 +# Meta platform tcl +# Meta summary TeX's docstrip written in Tcl +# Meta description This application is an implementation +# Meta description of TeX's docstrip application in Tcl. +# Meta description It provides commands to convert a docstrip +# Meta description weave according to a set of guards, to +# Meta description assemble an output based on several sets +# Meta description guards and input files, i.e. of a document +# Meta description spread over several inputs and/or guards, +# Meta description and to extract and list all unique guard +# Meta description expressions found in a document. +# Meta category Processing docstrip documents +# Meta subject docstrip TeX LaTeX +# Meta require docstrip +# Meta author Andreas Kupries +# Meta license BSD +# @@ Meta End + +package provide tcldocstrip 1.0.1 + +# TODO __________________________ +# Add handling of pre- and postambles. + +# tcldocstrip - Docstrip written in Tcl +# =========== = ======================= +# +# Use cases +# --------- +# +# (-) Providing access to the functionality of the tcllib/docstrip +# package from within shell and other scripts which are not Tcl. +# +# (1) Conversion of a single input file according to the listed +# guards into the stripped output. +# +# This handles the most simple case of a set of guards +# specifying a single document found in a single input file. +# +# (2) Stitching, or the assembly of an output from several sets of +# guards, in a specific order, and possibly from different +# files. This is the second common case. One document spread +# over several inputs, and/or spread over different guard sets. +# +# (3) Extraction and listing of all the unique guard expressions and +# guards used within a document to help a person which did not +# author the document in question in familiarizing itself with +# it. +# +# Command syntax +# -------------- +# +# Ad 1) tcldocstrip output|"-" ?options? input ?guards? +# +# Converts the input file according to the specified guards and +# options. The result is written to the named output. Usage of +# the string "-" as output signals that the result should be +# written to stdout. The guards are document-specific and have +# to be known to the caller. The options are the same as +# accepted by docstrip::extract. +# +# -metaprefix string +# -onerror mode {ignore,puts,throw} +# -trimlines bool +# +# Additional options understood are +# +# -premamble text +# -postamble text +# -nopremamble +# -nopostamble +# +# These are processed by the application itself. The -no*amble +# options deactivate pre- and postambles altogether, whereas the +# -*amble specify the _user_ part of pre- and postambles. This +# part can be empty, in that case only the standard parts are +# shown. This is the default. +# +# Ad 2) tcldocstrip ?options? output|"-" (?options? input|"." guards)... +# +# Extracts data from the various input files, according to the +# specified options and guards, and writes the result to the +# given output, in the order of their specification on the +# command line. Options specified before the output are global +# settings, whereas the options specified before each input are +# valid only just for this input file. Unspecified values are +# taken from the global settings. As in (1) "-" as output causes +# the application to write to stdout. Using "." for an input +# file signals that the last input file should be used +# again. This enables the assembly of the output from one input +# file using multiple and different sets of guards. +# +# Ad 3) tcldocstrip -guards input +# +# Determines the guards, and unique guard expressions used +# within the input document. The found strings are written to +# stdout, one string per line. +# + +lappend auto_path [file join [file dirname [file dirname [info script]]] modules] +package require docstrip + +# ### ### ### ######### ######### ######### +## Internal data and status + +namespace eval ::tcldocstrip { + + # List of global options and their arguments found in the command + # line. No checking was done on them, they are simply passed to + # the extraction command. + + variable options {} + + # List of input specifications. Each element is a list specifying + # the extraction options, input file, and guard set, in this + # order. + + variable stitch {} + + # Name of the file to write to. "-" signals that output has to be + # written to stdout. + + variable output {} + + # Mode of operation: Conversion, or guard retrieval + + variable mode Extract + + # The input file for guard retrieval mode. + + variable input {} + + # Standard preamble to preambles + + variable preamble {} + append preamble \n + append preamble "This is file `@output@'," \n + append preamble "generated with the tcldocstrip utility." \n + append preamble \n + append preamble "The original source files were:" \n + append preamble \n + append preamble "@input@ (with options: `@guards@')" \n + append preamble \n + + # Standard postamble to postambles + + variable postamble {} + append postamble \n + append postamble \n + append postamble "End of file `@output@'." + + # Default values for the options which are relevant to the + # application itself and thus have to be defined always. + # They are processed as global options, as part of argv. + + variable defaults {-metaprefix {%} -preamble {} -postamble {}} +} + +# ### ### ### ######### ######### ######### +## External data and status +# +## This tool does not depend on external data and/or status. + +# ### ### ### ######### ######### ######### +## Option processing. +## Validate command line. +## Full command line syntax. +## +# tcldocstrip ?-option value...? input ?guard...? +## + +proc ::tcldocstrip::processCmdline {} { + global argv + + variable defaults + variable preamble + variable postamble + variable options + variable stitch + variable output + variable input + variable mode + + # Process the options, perform basic validation. + + set optbuf {} + set stitchbuf {} + set get output + + if {![llength $argv]} { + set argv $defaults + } else { + set argv [eval [linsert $argv 0 linsert $defaults end]] + } + + while {[llength $argv]} { + set opt [lindex $argv 0] + if {($opt eq "-") || ![string match "-*" $opt]} { + # Non option state machine. Output first. Then input and + # guards alternating. + + set argv [lrange $argv 1 end] + switch -exact -- $get { + output { + set output $opt + set get input + } + input { + lappend stitchbuf $optbuf $opt + set optbuf {} + set get guards + } + guards { + lappend stitchbuf $opt + set get input + lappend stitch $stitchbuf + set stitchbuf {} + } + } + continue + } + + switch -exact -- $opt { + -guards { + if { + ($get ne "output") || + ([llength $argv] != 2) + } Usage + + set mode Guards + set input [lindex $argv 1] + break + } + -nopreamble - + -nopostamble { + set o -[string range $opt 3 end] + if {$get eq "output"} { + lappend options $o "" + } else { + lappend optbuf $o "" + } + } + -preamble { + set val $preamble[lindex $argv 1] + if {$get eq "output"} { + lappend options $opt $val + } else { + lappend optbuf $opt $val + } + set argv [lrange $argv 2 end] + } + -postamble { + set val [lindex $argv 1]$postamble + if {$get eq "output"} { + lappend options $opt $val + } else { + lappend optbuf $opt $val + } + set argv [lrange $argv 2 end] + } + default { + set val [lindex $argv 1] + if {$get eq "output"} { + lappend options $opt $val + } else { + lappend optbuf $opt $val + } + + set argv [lrange $argv 2 end] + } + } + } + + if {$get eq "guards"} { + # Complete last input spec, may have no guards. + lappend stitchbuf {} + lappend stitch $stitchbuf + set stitchbuf {} + } + + # Additional validation. + + if {$mode eq "Guards"} { + CheckInput $input {Input path} + return + } + + if {![llength $stitch]} { + Usage + } + + set first 1 + foreach in $stitch { + foreach {o i g} $in break + if {$first || ($i ne ".")} { + # First input file must not be ".". + CheckInput $i {Input path} + } + set first 0 + } + + CheckTheOutput + return +} + +# ### ### ### ######### ######### ######### +## Option processing. +## Helpers: Generation of error messages. +## I. General usage/help message. +## II. Specific messages. +# +# Both write their messages to stderr and then +# exit the application with status 1. +## + +proc ::tcldocstrip::Usage {} { + global argv0 + puts stderr "$argv0: ?options? output (?options? input guards)..." + puts stderr "$argv0: -guards input" + exit 1 +} + +proc ::tcldocstrip::ArgError {text} { + global argv0 + puts stderr "$argv0: $text" + exit 1 +} + +proc in {list item} { + expr {([lsearch -exact $list $item] >= 0)} +} + +# ### ### ### ######### ######### ######### +## Check existence and permissions of an input/output file or +## directory. + +proc ::tcldocstrip::CheckInput {f label} { + if {![file exists $f]} { + ArgError "Unable to find $label \"$f\"" + } elseif {![file readable $f]} { + ArgError "$label \"$f\" not readable (permission denied)" + } elseif {![file isfile $f]} { + ArgError "$label \"$f\" is not a file" + } + return +} + +proc ::tcldocstrip::CheckTheOutput {} { + variable output + + if {$output eq ""} { + ArgError "No output path specified" + } elseif {$output eq "-"} { + # Stdout. This is ok. + return + } + + set base [file dirname $output] + if {[string equal $base ""]} {set base [pwd]} + + if {![file exists $output]} { + if {![file exists $base]} { + ArgError "Output base path \"$base\" not found" + } + if {![file writable $base]} { + ArgError "Output base path \"$base\" not writable (permission denied)" + } + } elseif {![file writable $output]} { + ArgError "Output path \"$output\" not writable (permission denied)" + } elseif {![file isfile $output]} { + ArgError "Output path \"$output\" is not a file" + } + return +} + +# ### ### ### ######### ######### ######### +## Helper commands. File reading and writing. + +proc ::tcldocstrip::Get {f} { + variable data + if {[info exists data($f)]} {return $data($f)} + return [set data($f) [read [set in [open $f r]]][close $in]] +} + +proc ::tcldocstrip::Write {f data} { + puts -nonewline [set out [open $f w]] $data + close $out + return +} + +proc ::tcldocstrip::WriteStdout {data} { + puts -nonewline stdout $data + return +} + +# ### ### ### ######### ######### ######### +## Helper commands. Guard extraction. + +proc ::tcldocstrip::Guards {text} { + array set g {} + set verbatim 0 + set verbtag {} + foreach line [split $text \n] { + if {$verbatim} { + # End of verbatim mode + if {$line eq $verbtag} {set verbatim 0} + continue + } + switch -glob -- $line { + %<<* { + # Start of verbatim mode. + set verbatim 1 + set verbtag %[string range $line 3 end] + continue + } + %<* { + if {![regexp -- {^%<([*/+-]?)([^>]*)>(.*)$} \ + $line --> modifier expression line]} { + # Malformed guard. FUTURE Handle via -onerror. For now: ignore. + continue + } + # Remember the guard. Hashtable ensures that + # duplicates are removed automatically. + set g($expression) . + } + default {continue} + } + } + return [array names g] +} + + +# ### ### ### ######### ######### ######### +## Configuation phase, validate command line. + +::tcldocstrip::processCmdline + +# ### ### ### ######### ######### ######### +## Commands implementing the main functionality. + +proc ::tcldocstrip::Do.Extract {} { + variable stitch + variable output + variable options + + set text "" + + foreach in $stitch { + foreach {opt input guards} $in break + + # Merge defaults, global and local options, then filch the + # options handled in the application. + + unset -nocomplain o + array set o $options + array set o $opt + + set pre "" + if {[info exists o(-preamble)]} { + set pre $o(-preamble) + unset o(-preamble) + } + set post "" + if {[info exists o(-postamble)]} { + set post $o(-postamble) + unset o(-postamble) + } + + set opt [array get o] + set c $o(-metaprefix) + + set pmap [list \ + @output@ $output \ + @input@ $input \ + @guards@ $guards \ + ] + + if {$pre ne ""} { + append text $c $c " " [join [split [string map $pmap $pre] \n] "\n$c$c "] + } + + append text [eval [linsert $opt 0 docstrip::extract [Get $input] $guards]] + + if {$post ne ""} { + append text $c $c " " [join [split [string map $pmap $post] \n] "\n$c$c "] + } + } + + if {$output eq "-"} { + WriteStdout $text + } else { + Write $output $text + } + return +} + +proc ::tcldocstrip::Do.Guards {} { + variable input + + WriteStdout [join [lsort [Guards [Get $input]]] \n] + return +} + +# ### ### ### ######### ######### ######### +## Invoking the functionality. + +if {[catch { + set mode $::tcldocstrip::mode + ::tcldocstrip::Do.$mode +} msg]} { + ## puts $::errorInfo + ::tcldocstrip::ArgError $msg +} + +# ### ### ### ######### ######### ######### +exit + +# Generic internal command for error handling. Factored out of the +# implementation of extract into its own command. + +proc HandleError {text attr lineno} { + variable O + + switch -- [string tolower $O(-onerror)] "puts" { + puts stderr "docstrip: $text on line $lineno." + } "ignore" {} default { + return \ + -code error \ + -errorinfo "" \ + -errorcode [linsert $attr end $lineno] \ + $text + } +} diff --git a/tcllib/apps/tcldocstrip.man b/tcllib/apps/tcldocstrip.man new file mode 100644 index 0000000..aa35b7d --- /dev/null +++ b/tcllib/apps/tcldocstrip.man @@ -0,0 +1,197 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin tcldocstrip n 1.0] +[see_also docstrip] +[keywords .dtx] +[keywords conversion] +[keywords docstrip] +[keywords documentation] +[keywords LaTeX] +[keywords {literate programming}] +[keywords markup] +[keywords source] +[copyright {2005 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[titledesc {Tcl-based Docstrip Processor}] +[moddesc {Textprocessing toolbox}] +[category {Documentation tools}] +[description] +[para] + +The application described by this document, [syscmd tcldocstrip], is a +relative of [syscmd docstrip], a simple literate programming tool for +LaTeX. + +[para] + +[syscmd tcldocstrip] is based upon the package [package docstrip]. + +[subsection {USE CASES}] + +[syscmd tcldocstrip] was written with the following three use cases in +mind. + +[para] +[list_begin enumerated] +[enum] +Conversion of a single input file according to the listed guards into +the stripped output. This handles the most simple case of a set of +guards specifying a single document found in a single input file. + +[enum] +Stitching, or the assembly of an output from several sets of guards, +in a specific order, and possibly from different files. This is the +second common case. One document spread over several inputs, and/or +spread over different guard sets. + +[enum] +Extraction and listing of all the unique guard expressions and guards +used within a document to help a person which did not author the +document in question in familiarizing itself with it. + +[list_end] + +[subsection {COMMAND LINE}] + +[list_begin definitions] + +[call [cmd tcldocstrip] [arg output] [opt options] [arg input] [opt [arg guards]]] + +This is the form for use case [lb]1[rb]. It converts the [arg input] +file according to the specified [arg guards] and options. The result +is written to the named [arg output] file. + +Usage of the string [const "-"] as the name of the output signals that +the result should be written to [const stdout]. The guards are +document-specific and have to be known to the caller. The +[arg options] will be explained later, in section [sectref OPTIONS]. + +[list_begin arguments] + +[arg_def path output in] + +This argument specifies where to write the generated document. It can +be the path to a file or directory, or [const -]. + +The last value causes the application to write the generated +documented to [const stdout]. + +[para] + +If the [arg output] does not exist then [lb]file dirname $output[rb] +has to exist and must be a writable directory. + +[arg_def path inputfile in] + +This argument specifies the path to the file to process. It has to +exist, must be readable, and written in [term docstrip] format. + +[list_end] +[para] + +[call [cmd tcldocstrip] [opt options] [arg output] ([opt options] [arg input] [arg guards])...] + +This is the form for use case [lb]2[rb]. It differs from the form for +use case [lb]1[rb] by the possibility of having options before the +output file, which apply in general, and specifying more than one +inputfile, each with its own set of input specific options and guards. + +[para] + +It extracts data from the various [arg input] files, according to the +specified [arg options] and [arg guards], and writes the result to the +given [arg output], in the order of their specification on the command +line. Options specified before the output are global settings, whereas +the options specified before each input are valid only just for this +input file. Unspecified values are taken from the global settings, or +defaults. As for form [lb]1[rb] using the string [const "-"] as output +causes the application to write to stdout. + +Using the string [const "."] for an input file signals that the last +input file should be used again. This enables the assembly of the +output from one input file using multiple and different sets of +guards, without having to specify the full name of the file every +time. + +[call [cmd tcldocstrip] [option -guards] [arg input]] + +This is the form for use case [lb]3[rb]. + +It determines the guards, and unique guard expressions used within the +provided [arg input] document. The found strings are written to +stdout, one string per line. + +[list_end] + +[subsection OPTIONS] + +This section describes all the options available to the user of the +application, with the exception of the option [option -guards]. This +option was described already, in section [sectref {COMMAND LINE}]. + +[para] +[list_begin options] +[opt_def -metaprefix string] + +This option is inherited from the command [cmd docstrip::extract] +provided by the package [package docstrip]. + +[para] + +It specifies the string by which the '%%' prefix of a metacomment line +will be replaced. Defaults to '%%'. For Tcl code this would typically +be '#'. + +[opt_def -onerror mode] + +This option is inherited from the command [cmd docstrip::extract] +provided by the package [package docstrip]. + +[para] + +It controls what will be done when a format error in the [arg text] +being processed is detected. The settings are: + +[list_begin definitions] +[def [const ignore]] +Just ignore the error; continue as if nothing happened. + +[def [const puts]] +Write an error message to [const stderr], then continue processing. + +[def [const throw]] +Throw an error. [var ::errorCode] is set to a list whose first element +is [const DOCSTRIP], second element is the type of error, and third +element is the line number where the error is detected. This is the +default. +[list_end] + +[opt_def -trimlines bool] + +This option is inherited from the command [cmd docstrip::extract] +provided by the package [package docstrip]. + +[para] + +Controls whether [emph spaces] at the end of a line should be trimmed +away before the line is processed. Defaults to [const true]. + +[opt_def -preamble text] +[opt_def -postamble text] +[opt_def -nopreamble] +[opt_def -nopostamble] + +The -no*amble options deactivate file pre- and postambles altogether, +whereas the -*amble options specify the [emph user] part of the file +pre- and postambles. This part can be empty, in that case only the +standard parts are shown. This is the default. + +[para] + +Preambles, when active, are written before the actual content of a +generated file. In the same manner postambles are, when active, +written after the actual content of a generated file. + +[list_end] + +[vset CATEGORY docstrip] +[include ../modules/doctools2base/include/feedback.inc] +[manpage_end] |