The Official Supybot DocBook Metadocumentation (or, How Does One SGML File Turn Into All Those Document Formats?) Okay, though this isn't the case yet, ideally all of Supybot's documentation can and will be done using DocBook and DocBook-related tools as well as a few custom extensions that I've written. - How does DocBook work? First things first, you have to understand sort of how DocBook works in order to figure out how our documentation gets generated from just one file. What DocBook is, basically, is just a DTD (Document Type Definition). What that means is that it simply specifies how a document can be structured and still be considered a valid document by placing restrictions on what elements go where. It's a popular DTD because it is structured very well and it's not only fairly generic, but it also has nice elements that make documenting things (such as supybot) rather easy. It focuses on structure and content instead of presentation which is what makes it nice for writing things which are output format agnostic. So, let's say we've written a proper DocBook document, now what? Well, using an output formatting tool and a stylesheet, you create whatever form of output you want. What we use for producing the outptut is jade, and DocBook comes with a few stylesheets that work with that tool to create output formats like HTML and TeX. From the TeX file we produce a DVI (device independent) file with latex, and from that we produce the print formats of our documents, like PDF and Postscript using tools like dvips and dvipdfm. - What extra stuff do we do? Well, since our documents all have to do with an IRC bot, there are some very common things that we talk about a lot that we might like to format specially. For example, when we discuss a particular command for the bot we might like to have that text appear slightly different to emphasize the fact that it is special. So, for the commonly used items that weren't already covered by DocBook's DTD, I added elements into a new DTD which just extends DocBook's DTD. So now we have elements like <nick> and <channel> and <botcommand> that we can use for our documentation. Of course, now that we have used a DTD with more stuff in it (than DocBook), the stylesheets that DocBook provides won't do any special formatting for those new elements so we have to write new stylesheets as well. Once again I just extended the existing ones with formatting instructions on how to treat the new elements. So with this done, now our HTML and TeX (and whatever else) output will be properly formatted. - How do I make my own changes to the DTD and stylesheets? Primarily, you don't :) Ask me (Strike) first about it, and I will generally write them for you or explain a better way of doing things. This is especially true for the DTD, because that must remain consistent everywhere we write/read supybot docs based on it. The stylesheets are more lax and can be modified to produce whatever kind of output you wish. So, with that warning/reminder out of the way, here's how to modify each anyway. This doesn't really assume any knowledge of how to write a DTD, nor is it an exhaustive reference on writing one, so don't treat it as such. I'm basically just going to explain how to add extra elements that will play well with the DocBook DTD. -- Adding an element to the DTD If you've decided that there's a certain "thing" that's mentioned a lot in the documentation and deserves classification (for potential special formatting), you'll probably want to create a new element (or set of elements) for it. I'll walk you through how I added the "nick" element to our DTD (though many/most of the elements I added follow an identical process). The very first thing you need to figure out is: where in my document does this element "fit". That is to say, what elements should/can rightly contain this particular one? In the case of the "nick" element, it's basically always an inline-formatted deal that belongs in paragraphs for the most part. For those of you scratching your heads at that last sentence, perhaps thinking "okay, so how are we supposed to know what is relevant?" I say, "don't worry, I learned by example as well." Basically, I just looked through the DocBook DTD and figured out where things belong. Now, even if you don't know the DocBook DTD front-to-back, you can still peruse it to figure out where your new element belongs. Obviously, you should probably know *some* DocBook to figure out what each element means, but luckily all of our docs have been converted to DocBook and serve as nice examples of the usage of many elements :) Now, to figure out where something like "nick" belongs. In many ways, a nick is sort of like a variable name (at least in documentation usage). So, the element I chose to base it off of was "varname". If you have the DocBook DTD installed (as you should if you intend on making extensions to it), the varname element definition is contained in the dbpoolx.mod filename (in Debian, it's under /usr/share/sgml/docbook/dtd/4.2). How did I know this? Well, grep is your friend and mine too, and dbpoolx is the only filename that shows up when grepping for "varname" in the DocBook DTD directory. So, we open up dbpoolx.mod and search for varname. The first thing we find it in looks like this: <!ENTITY % tech.char.class "action|application |classname|methodname|interfacename|exceptionname |ooclass|oointerface|ooexception |command|computeroutput |database|email|envar|errorcode|errorname|errortype|errortext|filename |function|guibutton|guiicon|guilabel|guimenu|guimenuitem |guisubmenu|hardware|interface|keycap |keycode|keycombo|keysym|literal|constant|markup|medialabel |menuchoice|mousebutton|option|optional|parameter |prompt|property|replaceable|returnvalue|sgmltag|structfield |structname|symbol|systemitem|token|type|userinput|varname %ebnf.inline.hook; %local.tech.char.class;"> Hmm, this doesn't look like a definition of varname (to me, but I sort of cheated by having read about DocBook before-hand ;)), but it will be important to remember for later. Let's try and find the element definition for varname (so, basically, let's look for the first line that starts with "<!ELEMENT "). The first line I come up with when I search is: <!ELEMENT varname %ho; (%smallcptr.char.mix;)*> Rather than write a separate tutorial for interpreting DTDs, I found a good SGML tutorial online that explains everything necessary to help you parse the DocBook DTD to figure out what the varname element really is, as well as to help you learn all the stuff necessary for what we will cover in creating our new nick element. That tutorial is at http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html#howtodtd (it's for reading the HTML DTD, but it applies to any DTD). So, now that we understand how to write/read things for a DTD, we arrive at the time where we can write the actual definition of our "nick" element: <!ELEMENT Nick - - ((%smallcptr.char.mix;)+)> As we learned in the above tutorial, this means that we are creating an element named "nick", which must have start and end tags, and is defined to contain one or more of whatever is in "smallcptr.char.mix". And rather than hunt through the DocBook DTD to figure out what that is, for now we'll just live with the fact that whatever can go into a DocBook varname can go into our new nick element. If you feel so inclined, feel free to try and define the content model for nick to only include valid nick characters. It's perfectly doable, and I'll probably do it at some point but I haven't yet. Since we're extending the DocBook DTD, I also decided that it'd be nice to follow the element creation conventions observed in their DTD, so there are a few more lines associated with our new nick element. All of them are related to the attributes of the element, and allowing for them to be extended by external DTDs (much like we are doing, only we aren't changing attributes of existing elements, just adding our own). The first one is: <!ENTITY % local.nick.attrib ""> This basically defines an empty entity named local.nick.attrib which we will include so that if anyone chooses to extend the nick attributes, all they have to do is redefine local.nick.attrib. <!ENTITY % nick.role.attrib "%role.attrib;"> To tell you the truth, I'm not entirely sure what this is for, but it follows the DocBook convention :) <!ATTLIST Nick %common.attrib; %local.nick.attrib; %nick.role.attrib; > This is, of course, our attribute list for our nick element. It consists of the two things we just defined as well as common.attrib which contains things like "id" and whatnot which all DocBook elements are expected to have. -- Extending the DocBook DTD to recognize new elements So, that's all you need to define your new element. But, we're not done just yet! We're almost there, we just need to make it so that it works with the existing DocBook elements, otherwise it's no good to us. Since we defined our element to esentially be the same as varname, it probably belongs at the same place within the DocBook schema as varname. Do you remember when we had that large entity definition that wasn't what we were looking for at the time though I said it'd be important later? Well, later is now. So, what that line tells us is what class of elements DocBook has varname in, which is "tech.char.class". And thanks to the DocBook convention of defining a local.<classname> entity that we can extend, all we have to do is redefine local.tech.char.class to contain "nick", and we are done. You may notice, however, that we don't actually put varname right into the local.tech.char.class entity, but instead we create our own supybot.tech.char.class class of elements that are supybot-specific (and are the equivalent of DocBook's tech.char.class elements) and instead, put all of those into the local.tech.char.class entity. Basically, we just go through one more level of indirection.