jerojasro@336: \chapter{Adding functionality with extensions} jerojasro@336: \label{chap:hgext} jerojasro@336: jerojasro@336: While the core of Mercurial is quite complete from a functionality jerojasro@336: standpoint, it's deliberately shorn of fancy features. This approach jerojasro@336: of preserving simplicity keeps the software easy to deal with for both jerojasro@336: maintainers and users. jerojasro@336: jerojasro@336: However, Mercurial doesn't box you in with an inflexible command set: jerojasro@336: you can add features to it as \emph{extensions} (sometimes known as jerojasro@336: \emph{plugins}). We've already discussed a few of these extensions in jerojasro@336: earlier chapters. jerojasro@336: \begin{itemize} jerojasro@336: \item Section~\ref{sec:tour-merge:fetch} covers the \hgext{fetch} jerojasro@336: extension; this combines pulling new changes and merging them with jerojasro@336: local changes into a single command, \hgxcmd{fetch}{fetch}. jerojasro@336: \item In chapter~\ref{chap:hook}, we covered several extensions that jerojasro@336: are useful for hook-related functionality: \hgext{acl} adds access jerojasro@336: control lists; \hgext{bugzilla} adds integration with the Bugzilla jerojasro@336: bug tracking system; and \hgext{notify} sends notification emails on jerojasro@336: new changes. jerojasro@336: \item The Mercurial Queues patch management extension is so invaluable jerojasro@336: that it merits two chapters and an appendix all to itself. jerojasro@336: Chapter~\ref{chap:mq} covers the basics; jerojasro@336: chapter~\ref{chap:mq-collab} discusses advanced topics; and jerojasro@336: appendix~\ref{chap:mqref} goes into detail on each command. jerojasro@336: \end{itemize} jerojasro@336: jerojasro@336: In this chapter, we'll cover some of the other extensions that are jerojasro@336: available for Mercurial, and briefly touch on some of the machinery jerojasro@336: you'll need to know about if you want to write an extension of your jerojasro@336: own. jerojasro@336: \begin{itemize} jerojasro@336: \item In section~\ref{sec:hgext:inotify}, we'll discuss the jerojasro@336: possibility of \emph{huge} performance improvements using the jerojasro@336: \hgext{inotify} extension. jerojasro@336: \end{itemize} jerojasro@336: jerojasro@336: \section{Improve performance with the \hgext{inotify} extension} jerojasro@336: \label{sec:hgext:inotify} jerojasro@336: jerojasro@336: Are you interested in having some of the most common Mercurial jerojasro@336: operations run as much as a hundred times faster? Read on! jerojasro@336: jerojasro@336: Mercurial has great performance under normal circumstances. For jerojasro@336: example, when you run the \hgcmd{status} command, Mercurial has to jerojasro@336: scan almost every directory and file in your repository so that it can jerojasro@336: display file status. Many other Mercurial commands need to do the jerojasro@336: same work behind the scenes; for example, the \hgcmd{diff} command jerojasro@336: uses the status machinery to avoid doing an expensive comparison jerojasro@336: operation on files that obviously haven't changed. jerojasro@336: jerojasro@336: Because obtaining file status is crucial to good performance, the jerojasro@336: authors of Mercurial have optimised this code to within an inch of its jerojasro@336: life. However, there's no avoiding the fact that when you run jerojasro@336: \hgcmd{status}, Mercurial is going to have to perform at least one jerojasro@336: expensive system call for each managed file to determine whether it's jerojasro@336: changed since the last time Mercurial checked. For a sufficiently jerojasro@336: large repository, this can take a long time. jerojasro@336: jerojasro@336: To put a number on the magnitude of this effect, I created a jerojasro@336: repository containing 150,000 managed files. I timed \hgcmd{status} jerojasro@336: as taking ten seconds to run, even when \emph{none} of those files had jerojasro@336: been modified. jerojasro@336: jerojasro@336: Many modern operating systems contain a file notification facility. jerojasro@336: If a program signs up to an appropriate service, the operating system jerojasro@336: will notify it every time a file of interest is created, modified, or jerojasro@336: deleted. On Linux systems, the kernel component that does this is jerojasro@336: called \texttt{inotify}. jerojasro@336: jerojasro@336: Mercurial's \hgext{inotify} extension talks to the kernel's jerojasro@336: \texttt{inotify} component to optimise \hgcmd{status} commands. The jerojasro@336: extension has two components. A daemon sits in the background and jerojasro@336: receives notifications from the \texttt{inotify} subsystem. It also jerojasro@336: listens for connections from a regular Mercurial command. The jerojasro@336: extension modifies Mercurial's behaviour so that instead of scanning jerojasro@336: the filesystem, it queries the daemon. Since the daemon has perfect jerojasro@336: information about the state of the repository, it can respond with a jerojasro@336: result instantaneously, avoiding the need to scan every directory and jerojasro@336: file in the repository. jerojasro@336: jerojasro@336: Recall the ten seconds that I measured plain Mercurial as taking to jerojasro@336: run \hgcmd{status} on a 150,000 file repository. With the jerojasro@336: \hgext{inotify} extension enabled, the time dropped to 0.1~seconds, a jerojasro@336: factor of \emph{one hundred} faster. jerojasro@336: jerojasro@336: Before we continue, please pay attention to some caveats. jerojasro@336: \begin{itemize} jerojasro@336: \item The \hgext{inotify} extension is Linux-specific. Because it jerojasro@336: interfaces directly to the Linux kernel's \texttt{inotify} jerojasro@336: subsystem, it does not work on other operating systems. jerojasro@336: \item It should work on any Linux distribution that was released after jerojasro@336: early~2005. Older distributions are likely to have a kernel that jerojasro@336: lacks \texttt{inotify}, or a version of \texttt{glibc} that does not jerojasro@336: have the necessary interfacing support. jerojasro@336: \item Not all filesystems are suitable for use with the jerojasro@336: \hgext{inotify} extension. Network filesystems such as NFS are a jerojasro@336: non-starter, for example, particularly if you're running Mercurial jerojasro@336: on several systems, all mounting the same network filesystem. The jerojasro@336: kernel's \texttt{inotify} system has no way of knowing about changes jerojasro@336: made on another system. Most local filesystems (e.g.~ext3, XFS, jerojasro@336: ReiserFS) should work fine. jerojasro@336: \end{itemize} jerojasro@336: jerojasro@336: The \hgext{inotify} extension is not yet shipped with Mercurial as of jerojasro@336: May~2007, so it's a little more involved to set up than other jerojasro@336: extensions. But the performance improvement is worth it! jerojasro@336: jerojasro@336: The extension currently comes in two parts: a set of patches to the jerojasro@336: Mercurial source code, and a library of Python bindings to the jerojasro@336: \texttt{inotify} subsystem. jerojasro@336: \begin{note} jerojasro@336: There are \emph{two} Python \texttt{inotify} binding libraries. One jerojasro@336: of them is called \texttt{pyinotify}, and is packaged by some Linux jerojasro@336: distributions as \texttt{python-inotify}. This is \emph{not} the jerojasro@336: one you'll need, as it is too buggy and inefficient to be practical. jerojasro@336: \end{note} jerojasro@336: To get going, it's best to already have a functioning copy of jerojasro@336: Mercurial installed. jerojasro@336: \begin{note} jerojasro@336: If you follow the instructions below, you'll be \emph{replacing} and jerojasro@336: overwriting any existing installation of Mercurial that you might jerojasro@336: already have, using the latest ``bleeding edge'' Mercurial code. jerojasro@336: Don't say you weren't warned! jerojasro@336: \end{note} jerojasro@336: \begin{enumerate} jerojasro@336: \item Clone the Python \texttt{inotify} binding repository. Build and jerojasro@336: install it. jerojasro@336: \begin{codesample4} jerojasro@336: hg clone http://hg.kublai.com/python/inotify jerojasro@336: cd inotify jerojasro@336: python setup.py build --force jerojasro@336: sudo python setup.py install --skip-build jerojasro@336: \end{codesample4} jerojasro@336: \item Clone the \dirname{crew} Mercurial repository. Clone the jerojasro@336: \hgext{inotify} patch repository so that Mercurial Queues will be jerojasro@336: able to apply patches to your cope of the \dirname{crew} repository. jerojasro@336: \begin{codesample4} jerojasro@336: hg clone http://hg.intevation.org/mercurial/crew jerojasro@336: hg clone crew inotify jerojasro@336: hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches jerojasro@336: \end{codesample4} jerojasro@336: \item Make sure that you have the Mercurial Queues extension, jerojasro@336: \hgext{mq}, enabled. If you've never used MQ, read jerojasro@336: section~\ref{sec:mq:start} to get started quickly. jerojasro@336: \item Go into the \dirname{inotify} repo, and apply all of the jerojasro@336: \hgext{inotify} patches using the \hgxopt{mq}{qpush}{-a} option to jerojasro@336: the \hgxcmd{mq}{qpush} command. jerojasro@336: \begin{codesample4} jerojasro@336: cd inotify jerojasro@336: hg qpush -a jerojasro@336: \end{codesample4} jerojasro@336: If you get an error message from \hgxcmd{mq}{qpush}, you should not jerojasro@336: continue. Instead, ask for help. jerojasro@336: \item Build and install the patched version of Mercurial. jerojasro@336: \begin{codesample4} jerojasro@336: python setup.py build --force jerojasro@336: sudo python setup.py install --skip-build jerojasro@336: \end{codesample4} jerojasro@336: \end{enumerate} jerojasro@336: Once you've build a suitably patched version of Mercurial, all you jerojasro@336: need to do to enable the \hgext{inotify} extension is add an entry to jerojasro@336: your \hgrc. jerojasro@336: \begin{codesample2} jerojasro@336: [extensions] jerojasro@336: inotify = jerojasro@336: \end{codesample2} jerojasro@336: When the \hgext{inotify} extension is enabled, Mercurial will jerojasro@336: automatically and transparently start the status daemon the first time jerojasro@336: you run a command that needs status in a repository. It runs one jerojasro@336: status daemon per repository. jerojasro@336: jerojasro@336: The status daemon is started silently, and runs in the background. If jerojasro@336: you look at a list of running processes after you've enabled the jerojasro@336: \hgext{inotify} extension and run a few commands in different jerojasro@336: repositories, you'll thus see a few \texttt{hg} processes sitting jerojasro@336: around, waiting for updates from the kernel and queries from jerojasro@336: Mercurial. jerojasro@336: jerojasro@336: The first time you run a Mercurial command in a repository when you jerojasro@336: have the \hgext{inotify} extension enabled, it will run with about the jerojasro@336: same performance as a normal Mercurial command. This is because the jerojasro@336: status daemon needs to perform a normal status scan so that it has a jerojasro@336: baseline against which to apply later updates from the kernel. jerojasro@336: However, \emph{every} subsequent command that does any kind of status jerojasro@336: check should be noticeably faster on repositories of even fairly jerojasro@336: modest size. Better yet, the bigger your repository is, the greater a jerojasro@336: performance advantage you'll see. The \hgext{inotify} daemon makes jerojasro@336: status operations almost instantaneous on repositories of all sizes! jerojasro@336: jerojasro@336: If you like, you can manually start a status daemon using the jerojasro@336: \hgxcmd{inotify}{inserve} command. This gives you slightly finer jerojasro@336: control over how the daemon ought to run. This command will of course jerojasro@336: only be available when the \hgext{inotify} extension is enabled. jerojasro@336: jerojasro@336: When you're using the \hgext{inotify} extension, you should notice jerojasro@336: \emph{no difference at all} in Mercurial's behaviour, with the sole jerojasro@336: exception of status-related commands running a whole lot faster than jerojasro@336: they used to. You should specifically expect that commands will not jerojasro@336: print different output; neither should they give different results. jerojasro@336: If either of these situations occurs, please report a bug. jerojasro@336: jerojasro@336: \section{Flexible diff support with the \hgext{extdiff} extension} jerojasro@336: \label{sec:hgext:extdiff} jerojasro@336: jerojasro@336: Mercurial's built-in \hgcmd{diff} command outputs plaintext unified jerojasro@336: diffs. jerojasro@336: \interaction{extdiff.diff} jerojasro@336: If you would like to use an external tool to display modifications, jerojasro@336: you'll want to use the \hgext{extdiff} extension. This will let you jerojasro@336: use, for example, a graphical diff tool. jerojasro@336: jerojasro@336: The \hgext{extdiff} extension is bundled with Mercurial, so it's easy jerojasro@336: to set up. In the \rcsection{extensions} section of your \hgrc, jerojasro@336: simply add a one-line entry to enable the extension. jerojasro@336: \begin{codesample2} jerojasro@336: [extensions] jerojasro@336: extdiff = jerojasro@336: \end{codesample2} jerojasro@336: This introduces a command named \hgxcmd{extdiff}{extdiff}, which by jerojasro@336: default uses your system's \command{diff} command to generate a jerojasro@336: unified diff in the same form as the built-in \hgcmd{diff} command. jerojasro@336: \interaction{extdiff.extdiff} jerojasro@336: The result won't be exactly the same as with the built-in \hgcmd{diff} jerojasro@336: variations, because the output of \command{diff} varies from one jerojasro@336: system to another, even when passed the same options. jerojasro@336: jerojasro@336: As the ``\texttt{making snapshot}'' lines of output above imply, the jerojasro@336: \hgxcmd{extdiff}{extdiff} command works by creating two snapshots of jerojasro@336: your source tree. The first snapshot is of the source revision; the jerojasro@336: second, of the target revision or working directory. The jerojasro@336: \hgxcmd{extdiff}{extdiff} command generates these snapshots in a jerojasro@336: temporary directory, passes the name of each directory to an external jerojasro@336: diff viewer, then deletes the temporary directory. For efficiency, it jerojasro@336: only snapshots the directories and files that have changed between the jerojasro@336: two revisions. jerojasro@336: jerojasro@336: Snapshot directory names have the same base name as your repository. jerojasro@336: If your repository path is \dirname{/quux/bar/foo}, then \dirname{foo} jerojasro@336: will be the name of each snapshot directory. Each snapshot directory jerojasro@336: name has its changeset ID appended, if appropriate. If a snapshot is jerojasro@336: of revision \texttt{a631aca1083f}, the directory will be named jerojasro@336: \dirname{foo.a631aca1083f}. A snapshot of the working directory won't jerojasro@336: have a changeset ID appended, so it would just be \dirname{foo} in jerojasro@336: this example. To see what this looks like in practice, look again at jerojasro@336: the \hgxcmd{extdiff}{extdiff} example above. Notice that the diff has jerojasro@336: the snapshot directory names embedded in its header. jerojasro@336: jerojasro@336: The \hgxcmd{extdiff}{extdiff} command accepts two important options. jerojasro@336: The \hgxopt{extdiff}{extdiff}{-p} option lets you choose a program to jerojasro@336: view differences with, instead of \command{diff}. With the jerojasro@336: \hgxopt{extdiff}{extdiff}{-o} option, you can change the options that jerojasro@336: \hgxcmd{extdiff}{extdiff} passes to the program (by default, these jerojasro@336: options are ``\texttt{-Npru}'', which only make sense if you're jerojasro@336: running \command{diff}). In other respects, the jerojasro@336: \hgxcmd{extdiff}{extdiff} command acts similarly to the built-in jerojasro@336: \hgcmd{diff} command: you use the same option names, syntax, and jerojasro@336: arguments to specify the revisions you want, the files you want, and jerojasro@336: so on. jerojasro@336: jerojasro@336: As an example, here's how to run the normal system \command{diff} jerojasro@336: command, getting it to generate context diffs (using the jerojasro@336: \cmdopt{diff}{-c} option) instead of unified diffs, and five lines of jerojasro@336: context instead of the default three (passing \texttt{5} as the jerojasro@336: argument to the \cmdopt{diff}{-C} option). jerojasro@336: \interaction{extdiff.extdiff-ctx} jerojasro@336: jerojasro@336: Launching a visual diff tool is just as easy. Here's how to launch jerojasro@336: the \command{kdiff3} viewer. jerojasro@336: \begin{codesample2} jerojasro@336: hg extdiff -p kdiff3 -o '' jerojasro@336: \end{codesample2} jerojasro@336: jerojasro@336: If your diff viewing command can't deal with directories, you can jerojasro@336: easily work around this with a little scripting. For an example of jerojasro@336: such scripting in action with the \hgext{mq} extension and the jerojasro@336: \command{interdiff} command, see jerojasro@336: section~\ref{mq-collab:tips:interdiff}. jerojasro@336: jerojasro@336: \subsection{Defining command aliases} jerojasro@336: jerojasro@336: It can be cumbersome to remember the options to both the jerojasro@336: \hgxcmd{extdiff}{extdiff} command and the diff viewer you want to use, jerojasro@336: so the \hgext{extdiff} extension lets you define \emph{new} commands jerojasro@336: that will invoke your diff viewer with exactly the right options. jerojasro@336: jerojasro@336: All you need to do is edit your \hgrc, and add a section named jerojasro@336: \rcsection{extdiff}. Inside this section, you can define multiple jerojasro@336: commands. Here's how to add a \texttt{kdiff3} command. Once you've jerojasro@336: defined this, you can type ``\texttt{hg kdiff3}'' and the jerojasro@336: \hgext{extdiff} extension will run \command{kdiff3} for you. jerojasro@336: \begin{codesample2} jerojasro@336: [extdiff] jerojasro@336: cmd.kdiff3 = jerojasro@336: \end{codesample2} jerojasro@336: If you leave the right hand side of the definition empty, as above, jerojasro@336: the \hgext{extdiff} extension uses the name of the command you defined jerojasro@336: as the name of the external program to run. But these names don't jerojasro@336: have to be the same. Here, we define a command named ``\texttt{hg jerojasro@336: wibble}'', which runs \command{kdiff3}. jerojasro@336: \begin{codesample2} jerojasro@336: [extdiff] jerojasro@336: cmd.wibble = kdiff3 jerojasro@336: \end{codesample2} jerojasro@336: jerojasro@336: You can also specify the default options that you want to invoke your jerojasro@336: diff viewing program with. The prefix to use is ``\texttt{opts.}'', jerojasro@336: followed by the name of the command to which the options apply. This jerojasro@336: example defines a ``\texttt{hg vimdiff}'' command that runs the jerojasro@336: \command{vim} editor's \texttt{DirDiff} extension. jerojasro@336: \begin{codesample2} jerojasro@336: [extdiff] jerojasro@336: cmd.vimdiff = vim jerojasro@336: opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' jerojasro@336: \end{codesample2} jerojasro@336: jerojasro@336: \section{Cherrypicking changes with the \hgext{transplant} extension} jerojasro@336: \label{sec:hgext:transplant} jerojasro@336: jerojasro@336: Need to have a long chat with Brendan about this. jerojasro@336: jerojasro@336: \section{Send changes via email with the \hgext{patchbomb} extension} jerojasro@336: \label{sec:hgext:patchbomb} jerojasro@336: jerojasro@336: Many projects have a culture of ``change review'', in which people jerojasro@336: send their modifications to a mailing list for others to read and jerojasro@336: comment on before they commit the final version to a shared jerojasro@336: repository. Some projects have people who act as gatekeepers; they jerojasro@336: apply changes from other people to a repository to which those others jerojasro@336: don't have access. jerojasro@336: jerojasro@336: Mercurial makes it easy to send changes over email for review or jerojasro@336: application, via its \hgext{patchbomb} extension. The extension is so jerojasro@336: namd because changes are formatted as patches, and it's usual to send jerojasro@336: one changeset per email message. Sending a long series of changes by jerojasro@336: email is thus much like ``bombing'' the recipient's inbox, hence jerojasro@336: ``patchbomb''. jerojasro@336: jerojasro@336: As usual, the basic configuration of the \hgext{patchbomb} extension jerojasro@336: takes just one or two lines in your \hgrc. jerojasro@336: \begin{codesample2} jerojasro@336: [extensions] jerojasro@336: patchbomb = jerojasro@336: \end{codesample2} jerojasro@336: Once you've enabled the extension, you will have a new command jerojasro@336: available, named \hgxcmd{patchbomb}{email}. jerojasro@336: jerojasro@336: The safest and best way to invoke the \hgxcmd{patchbomb}{email} jerojasro@336: command is to \emph{always} run it first with the jerojasro@336: \hgxopt{patchbomb}{email}{-n} option. This will show you what the jerojasro@336: command \emph{would} send, without actually sending anything. Once jerojasro@336: you've had a quick glance over the changes and verified that you are jerojasro@336: sending the right ones, you can rerun the same command, with the jerojasro@336: \hgxopt{patchbomb}{email}{-n} option removed. jerojasro@336: jerojasro@336: The \hgxcmd{patchbomb}{email} command accepts the same kind of jerojasro@336: revision syntax as every other Mercurial command. For example, this jerojasro@336: command will send every revision between 7 and \texttt{tip}, jerojasro@336: inclusive. jerojasro@336: \begin{codesample2} jerojasro@336: hg email -n 7:tip jerojasro@336: \end{codesample2} jerojasro@336: You can also specify a \emph{repository} to compare with. If you jerojasro@336: provide a repository but no revisions, the \hgxcmd{patchbomb}{email} jerojasro@336: command will send all revisions in the local repository that are not jerojasro@336: present in the remote repository. If you additionally specify jerojasro@336: revisions or a branch name (the latter using the jerojasro@336: \hgxopt{patchbomb}{email}{-b} option), this will constrain the jerojasro@336: revisions sent. jerojasro@336: jerojasro@336: It's perfectly safe to run the \hgxcmd{patchbomb}{email} command jerojasro@336: without the names of the people you want to send to: if you do this, jerojasro@336: it will just prompt you for those values interactively. (If you're jerojasro@336: using a Linux or Unix-like system, you should have enhanced jerojasro@336: \texttt{readline}-style editing capabilities when entering those jerojasro@336: headers, too, which is useful.) jerojasro@336: jerojasro@336: When you are sending just one revision, the \hgxcmd{patchbomb}{email} jerojasro@336: command will by default use the first line of the changeset jerojasro@336: description as the subject of the single email message it sends. jerojasro@336: jerojasro@336: If you send multiple revisions, the \hgxcmd{patchbomb}{email} command jerojasro@336: will usually send one message per changeset. It will preface the jerojasro@336: series with an introductory message, in which you should describe the jerojasro@336: purpose of the series of changes you're sending. jerojasro@336: jerojasro@336: \subsection{Changing the behaviour of patchbombs} jerojasro@336: jerojasro@336: Not every project has exactly the same conventions for sending changes jerojasro@336: in email; the \hgext{patchbomb} extension tries to accommodate a jerojasro@336: number of variations through command line options. jerojasro@336: \begin{itemize} jerojasro@336: \item You can write a subject for the introductory message on the jerojasro@336: command line using the \hgxopt{patchbomb}{email}{-s} option. This jerojasro@336: takes one argument, the text of the subject to use. jerojasro@336: \item To change the email address from which the messages originate, jerojasro@336: use the \hgxopt{patchbomb}{email}{-f} option. This takes one jerojasro@336: argument, the email address to use. jerojasro@336: \item The default behaviour is to send unified diffs (see jerojasro@336: section~\ref{sec:mq:patch} for a description of the format), one per jerojasro@336: message. You can send a binary bundle instead with the jerojasro@336: \hgxopt{patchbomb}{email}{-b} option. jerojasro@336: \item Unified diffs are normally prefaced with a metadata header. You jerojasro@336: can omit this, and send unadorned diffs, with the jerojasro@336: \hgxopt{patchbomb}{email}{--plain} option. jerojasro@336: \item Diffs are normally sent ``inline'', in the same body part as the jerojasro@336: description of a patch. This makes it easiest for the largest jerojasro@336: number of readers to quote and respond to parts of a diff, as some jerojasro@336: mail clients will only quote the first MIME body part in a message. jerojasro@336: If you'd prefer to send the description and the diff in separate jerojasro@336: body parts, use the \hgxopt{patchbomb}{email}{-a} option. jerojasro@336: \item Instead of sending mail messages, you can write them to an jerojasro@336: \texttt{mbox}-format mail folder using the jerojasro@336: \hgxopt{patchbomb}{email}{-m} option. That option takes one jerojasro@336: argument, the name of the file to write to. jerojasro@336: \item If you would like to add a \command{diffstat}-format summary to jerojasro@336: each patch, and one to the introductory message, use the jerojasro@336: \hgxopt{patchbomb}{email}{-d} option. The \command{diffstat} jerojasro@336: command displays a table containing the name of each file patched, jerojasro@336: the number of lines affected, and a histogram showing how much each jerojasro@336: file is modified. This gives readers a qualitative glance at how jerojasro@336: complex a patch is. jerojasro@336: \end{itemize} jerojasro@336: jerojasro@336: %%% Local Variables: jerojasro@336: %%% mode: latex jerojasro@336: %%% TeX-master: "00book" jerojasro@336: %%% End: