libferris: FAQ

You asked them, you can't unask them!

Frequently Asked Questions


1. Building

1.1. Why don't you submit your Redhat rpms into the Fedora project?

This will probably happen in the future. I started making the rpms for libferris' dependancies well before Fedora started. There are many rpms that I have for the dependancies for libferris and it will take a while to feed them into the mainline Fedora project. The other point to note is that time spent packaging is time not spent coding and I tend to prefer the latter.

If anyone wants to feed Fedora then check the main mailing list to make sure nobody has expressed that they want to do it and then mail the list to express your interest. A good place to start for this would be the rpms that I make available on the download page for libferris.

1.2. What do I do if the build fails?

Make sure you have read this FAQ build section completely. Make sure you have all the latest versions of each library that ferris needs. If its a compile error then send an email to the witme-ferris mailing list. Remember that if build errors are not reported I'll assume that it builds just as dandy fine for everyone as it does for me.

1.3. Why are the latest packages for libferris out of date compared to to most recent tarball?

Most of the packages provided are the dependancies for libferris, these don't change that frequently but libferris is released rather more often. You should be able to rebuild rpm files from the libferris distribution tarballs when you have the dependancy pacakges installed.

1.4. Are you likely to provide deb files sometime soon?

As I don't use debian I myself am unlikely to provide deb files. I am happy to allow a contributor to package debs, anyone interested might like to email me when they have some stuff together.

1.5. Why do you seem to have this big love with STLPort?

Many of the IOStreams implementations that shipped with gcc in the past were not standard compliant. I like using the STLPort 4.5+ because it gives me a fixed codebase for STL and IOStreams to build upon.

1.6. What is the minimum version of gcc required for ferris?

gcc 3.0 should work, but 3.1+ is recommended.

1.7. What version of gcc are the developers using?

As at August 2005 gcc-4.0.1-4.fc4.

As at March 2005 a prerelease of gcc 4.0, gcc-4.0.0-0.32.src.rpm.

As at 17 Aug 2003 it was what came with redhat9. As of 18 Aug 2003 an upgrade to rawhide gcc-3.3.1-1 was done to fix build issues with gcc 3.3+.

1.8. I get build errors when attempting to make ferris stuff with gcc 3.3

Some of the parts of the ferris suite contained code that wouldn't compile with gcc 3.3+. This was due to a bug in gcc < 3.3 not checking access restrictions on templates.

You'll need atleast ferrisloki-2.0.0, fampp2-3.5.0, ferrisstreams-0.3.0, stldb4-0.3.2 and libferris-1.1.8 to build using gcc 3.3.

If you get the following error, download newer tarballs and try again.

g++ <snip>...</snip> -c Streams.cpp -MT Streams.lo -MD -MP -MF
.deps/Streams.TPlo -fPIC -DPIC -o .libs/Streams.lo

In file included from Streams.cpp:31:
../FerrisStreams/Streams.hh: In instantiation of
`Loki::SmartPtr<Ferris::ferris_streambuf<char, _STL::char_traits<char> >,
FerrisLoki::FerrisExRefCounted, Loki::DisallowConversion,
FerrisLoki::FerrisExSmartPointerChecker, FerrisLoki::FerrisExSmartPtrStorage>':
../FerrisStreams/Streams.hh:962: instantiated from
`Ferris::Ferris_commonstream<char, _STL::char_traits<char> >'
../FerrisStreams/Streams.hh:3115: instantiated from here
/usr/local/include/FerrisLoki/Extensions.hh:254: error: `typedef class
Ferris::ferris_streambuf<char, _STL::char_traits<char>
_STL::char_traits<char> > >::PointerType' is protected
../FerrisStreams/Streams.hh:962: error: within this context
/usr/local/include/FerrisLoki/Extensions.hh:253: error: `typedef class
Ferris::ferris_streambuf<char, _STL::char_traits<char>
_STL::char_traits<char> > >::StoredType' is protected
../FerrisStreams/Streams.hh:962: error: within this context

1.9. I'm getting an internal compiler error with gcc 3.3 and fampp2?

Make sure you have fampp2-3.5.0 or later. Earlier versions created an object factory in a manner that gcc 3.3 didn't like.

1.10. Seems that the build fails to find 'cstddef' using the STLPort rpm files you provide?

It is infortunate but there is a direct reference to the C++ header file path in the stlport headers.

The file with the references is /PREFIX/include/stlport/config/stl_gcc.h. Both of the references are on consecutive lines starting at roughly line 253 and are shown below. Changing the 3.3.1 to the version of gcc you are using should work fine.

# define _STLP_NATIVE_INCLUDE_PATH /usr/include/c++/3.3.1
# define _STLP_NATIVE_OLD_STREAMS_INCLUDE_PATH /usr/include/c++/3.3.1/backward

1.11. I'm getting undefined reference to foo( _STL::basic_string < char, _STL::char_traits < char > ,_STL::allocator < char > > const& ); when linking?

Basically if you get a link error with std:: stuff or _STL:: stuff in its API like the string example above it usually means that STLPort is being used by libferris and not the underlying library providing the API call.

The easiest method to fix this for libferris is to rebuild the offending library using STLPort for its STL/IOStreams.

To build a client library against an installed STLPort just exporting CXXFLAGS and LDFLAGS to contain stlport stuff should work. The below is what I am using, which you might need to adjust depending on your local install paths etc. I am also building for 64bit IO which you may or may not be doing.

$ stlport-config --cflags
-I/usr/include/stlport -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
$ stlport-config --libs
-L/usr/lib/lib -lstlport_gcc -lpthread -lstdc++ -lm

The plan is at some stage to move to using string::c_str() for API calls to C++ libraries to avoid this though that will likely be optional because it will involve a new string object creation on the called libraries side for each string passed. Likely there will be some black magic to get this marshalling to work by passing the native string if the client library was built against STLPort or passing c_str() if the client can not properly handle STLPort strings.

1.12. I have installed the lucene package for my distribution but after looking at the config.log for libferris it is not properly detecting it.

For some strange reason many distributions of lucene do not include the gcjh headers in a devel package. To use lucene from libferris the headers need to exist somewhere. The below script will generate the headers from your lucene jar file.

jar tvf /usr/share/java/lucene.jar | cut -c 36-1000 >|classes
for if in `cat classes `; do mkdir -p `dirname $if`; done
for if in `cat classes `; do
gcjh --classpath=/usr/share/java/lucene.jar \
`dirname $if`/`basename $if .class`;

Note that some versions of gcjh will generate headers which have some "redeclarations" of static symbols. If you get a few errors like the below when compiling libferris lucene support then simply edit the headers and append a unique postfix to the static symbol name. The static symbols in question are not used by libferris directly so there is no problem building against the edited headers.

error: declaration of 'JArray<jint>*
error: conflicts with previous declaration
'static void org::apache::lucene::analysis::standard::StandardTokenizer::jj_la1_0()'

1.13. There are problems compiling the Evolution mounting support.

Unfortunately many projects have header files laced with C++ keywords. The Evolution headers include the use of keywords delete, new, template, class in a few places. As their use is restricted to function prototypes for the most part you can simply rename the function arguments in your headers to get libferris to compile.

Sometimes patches to clean C++ use of other projects headers take time to filter into the other projects.

1.14. Does libferris work on OSX?

There have been an increasing number of folks asking this question. The short answer is that I don't have the hardware or software to verify if libferris builds OK on OSX.

2. Extra Installation steps?

2.1. How do I mount emacs (libferris version 1.1.72+)?

To allow libferris enabled clients access to your (x)emacs sessions you have to be running gnuserv in your emacs session and have a little bit of lisp glue called which defines some functions used by libferris to perform its work.

Add the below snippit of lisp to your ~/.emacs file.

; load ferris lisp code
(load "/usr/local/lib/ferris/emacs/libferris-emacs.el")

; if not already there.

Note that the initial implementation is geared towards smallish buffers. For example getting a C++ iostream for an xemacs buffer will cause that entire buffer to be transfered from emacs. In the future the IOStream could acquire parts of the buffer on demand to allow applications to move around in very large emacs buffers from a C++ IOStream.

The below is a little example of using xemacs mounting. Note that you can just as easily copy an emacs buffer into a varchar field in PostgreSQL as you can cat it.

# home == username == saru
$ ferrisls -0 emacs://localhost/saru
*Buffer List* Buffer Menu
*SGML LOG* Fundamental
*Warnings* Fundamental
*scratch* Lisp Interaction
TODO Fundamental /home/saru/TODO
faq.xml XML /www/libferris.web/src/...
libferrisemacs.cpp C++ /ferris/plugins/...
$ date >/tmp/datefile
$ gnuclient -q /tmp/datefile
$ fcat emacs://localhost/saru/datefile
Wed Dec 21 10:30:28 EST 2005
$ date | ferris-redirect -T emacs://localhost/saru/datefile
$ fcat emacs://localhost/saru/datefile
Wed Dec 21 10:32:05 EST 2005

2.2. How do I mount Firefox (libferris version 1.1.80+)?

To allow libferris enabled clients access to your Firefox sessions you have to install the firefox extension which comes with libferris. You'll find this in the plugins/context/firefox/firefox-extension directory with the name of libferrismount.xpi. This extension needs JSLib installed before it.

Support extends to getting easy access to anchor tags, image tags, and the Document Object Model for each web page in each tab of Firefox. You can also redirect information to a new firefox tab by writing to a special URL as shown below.

# home == username == ben
$ alias fls=ferrisls
$ fls -0 firefox://localhost/ben
$ fls -0 firefox://localhost/ben/by-title
Fedora Project, sponsored by Red Hat Fedora Project, sponsored by Red Hat
... file:///usr/share/doc/HTML/index.html
$ fls -0 'firefox://localhost/ben/by-title/Fedora Project, sponsored by Red Hat'
$ fls --show-ea=name,width,height \
'firefox://localhost/ben/by-title/Fedora Project, sponsored by Red Hat/images'
Fedora_Desktop.png 800 600
header-download.png 36 36
header-faq.png 36 36
header-fedora_logo.png 110 40
header-projects.png 36 36
important.png 34 34
note.png 34 34
tip.png 34 34
warning.png 34 34
$ ferriscp 'firefox://localhost/ben/by-title/Fedora..../images/warning.png' /tmp/tmpimg3
$ fls -0 'firefox://localhost/ben/by-title/Fedora Project, sponsored by Red Hat/links'
1. Welcome to Fedora Core 4 file:///usr/share/...
1.1. New in Fedora Core 4 file:///usr/share/...
$ fls -0 'firefox://localhost/ben/by-title/Fedora Project, sponsored by Red Hat/dom/HTML/BODY'
$ fcat 'firefox://localhost/ben/by-title/'
Fedora Core 4 Release Notes
$ echo hi there | ferris-redirect -T 'firefox://localhost/ben'
# new tab showing virtual document is now shown in FF.

2.3. How do I mount LDAP?

Assuming you have an LDAP server setup, for example something like openLDAP is setup and you know you can view entries from the command line. For info on setting up openLDAP see the Quick-Start Guide.

The below example assumes you have a very simple setup as described in the Quick-Start guide mentioned above. Prior to libferris version 1.1.80 you should run ferris-capplet-auth and create a localhost server setting the LDAP basedn to your local basedn, for example something like "dc=my-domain,dc=com" or check the "lookup basedn from server" for localhost. As of libferris 1.1.80 and above, if there is no explicit basedn for the server the default is to "lookup basedn from server". This new default should work well for servers which allow anonymous bindings.

$ fls ldap://
$ fls ldap://localhost
$ fls --xml ldap://localhost
<ferrisls url="ldap:///localhost" name="localhost" >
<context name="Manager" objectClass="organizationalRole" cn="Manager" />

2.4. Extra steps for maemo?

Active views don't work on some maemo versions which as of late 2010 includes the n900. You can turn these off using:

echo -n ".*" | ferris-redirect -T ~/.ferris/general.db/cfg-force-passive-view

3. Migration from old ferris versions

3.1. How do I update my personal RDF store from 1.1.72 to 1.1.80 format?

Instead of attaching out of band EA directly to a URI in libferris 1.1.80 it is attached to a unique metadata bundle and files in the filesystem are associated with metadata bundles to find their EA and remembrance data.

In the migration subdirectory is a client called convert-myrdf-1172-to-1173 which when run with no arguments will update the structure of your personal RDF store in ~/.ferris.

3.2. Remembrance RDF has changed in 1.1.71 from 1.1.70, can I update my existing RDF store to the new format?

Some of the URL nodes in 1.1.70 were literal nodes instead of URI nodes. It's not legal RDF the way it was and its fairly mechanical to update this issue. The other major change is that command line arguments are now quoted for use in bash etc in stored command history. There is no easy mechanical update for this part.

# put this into 1171.awk
FS=", [\\(\\[]"
pred=substr( $2, 0, length($2)-1 );
obj =substr( $3, 0, length($3)-2 );

if( length( $pred ) && length( $obj ) )
print "rdfproc myrdf add \"" subj "\" \"" pred "\" \"_:" obj "\"";

$ cd ~/.ferris/rdfdb
$ rdfproc myrdf find - ferris-file-view-history - \
| sed 's/Matched triple: {//g' \
| awk -f 1171.awk \
| bash

Don't forget to check out remembrance:// and branchfs-remembrance added in 1.1.71. With these you can query for files recently viewed and see the view and edit history for each file respectively. Using ferris-file-action -v will perform a "view" action on a file which will cause an entry for viewing in both of the above query filesystems.

3.3. older than 1.1.15 to 1.1.16: myrdf:// is empty

In ferris 1.1.16 the "myrdf://" URL resoves to "~/.ferris/rdfdb/myrdf" instead of just the directory. This means that a database name "myrdf" is being used to access the default RDF database. You should be able to rename the RDF/bdb files giving them a myrdf prefix and all is well.

$ cd ~/.ferris/rdfdb
$ mv -p2so.db myrdf-p2so.db
... and so on for other db files

If you are still getting errors listing myrdf:// then assert the following triples using the redland RDF command below. You should have this command as libferris requires the redland library to build. These commands may be supplemented in later libferris builds when RDFS is taken more into account.

$ cd ~/.ferris/rdfdb
$ rdfproc myrdf add ''
'' ''
$ rdfproc myrdf add ''
'' ''
$ rdfproc myrdf add ''
'' ''
$ rdfproc myrdf add ''
'' ''

4. Indexing filesystems and querying them

4.1. I am seeing exceptions in the output of feaindexadd or findexadd which contain some files which have non ascii chars in their names.

Unix makes no assumptions about what character encoding your filenames are in. However, most Linux installations use UTF8 for filename encoding. libferris assumes that your file and directory names are encoded in UTF8. This "just works" most of the time.

You can try making a copy of the file and renaming it using a tool such as convmv. With convmv you can automatically convert whole directory trees to have the same name but use the UTF8 encoding.

See also here for more information on unicode and Linux.

4.2. Creating PostgreSQL EAIndexes is failing with something about language not found and mentioning createlang?

To make adding new files to the database and reindexing files fast libferris uses some PLSQL 4GL code with the database. The PostgreSQL indexing module creates a new database with tables and indexes and as part of setting up that database it adds some server side PLSQL functions. You will need to have enabled PLSQL as the default for new databases for libferris to be able to add its PLSQL to the new database.

You can do this by issuing as root

# createlang -d template1 plpgsql

Future versions of libferris may install a template database with plsql enabled in it by default so that you don't have to enable plsql scripting as the defualt for new databases. Patches accepted :)

4.3. Creating PostgreSQL TSearch2 fulltext indexes is failing, is there anything I need to do to setup TSearch2?

The TSearch2 fulltext module will create its database from a specific template database. The template database is assumed to be setup to allow for TSearch2 functionality.

You can do this by issuing as root

# psql template1
template1=# create database ferrisftxtemplate;
template1=# \q
# psql ferrisftxtemplate </usr/share/pgsql/contrib/tsearch2.sql
# psql ferrisftxtemplate;
ferrisftxtemplate=# grant all on table pg_ts_cfg to public;
ferrisftxtemplate=# grant all on table pg_ts_cfgmap to public;
ferrisftxtemplate=# grant all on table pg_ts_dict to public;
ferrisftxtemplate=# grant all on table pg_ts_parser to public;
ferrisftxtemplate=# VACUUM FULL;
ferrisftxtemplate=# update pg_database set datistemplate = true where datname='ferrisftxtemplate';

It would be nice to be able to make the pg_ts_* tables owned by the user who creates a new database from the template. This functionality will be added to libferris when PostgreSQL supports ownership changes for tables in template databases.

4.4. What is this Formal Concept Analysis crazy thing you mention and how do I take advantage of it with libferris?

Formal Concept Analysis (FCA) is a data driven method to derive a lattice exposing the natural clustering in the input data. Once you have built a very large index with libferris you can easily pose one off questions like "find me all files less than 3 disk blocks in size". Using FCA you can pose many such questions, perhaps adding a time dimension to the above query to see if there was a particular time that a bunch of small files was spewed onto your drives.

I'll spare the details of FCA because there are already many books explaining it from maths and comp sci perspectives. To use FCA with libferris you first have to create an EA index. This step is explained in some detail in the Feb 2005 issue of the Linux Journal.

You'll also need to download, compile and install apriori-4.24-pg.tar.bz2 from the downloads page. The PostgreSQL enchancements in this package are being fed back into the mainline apriori distro by Christian Borgelt.

Given the path to your EA index, say EAPATH, creating a virtual filesystem using FCA is really quite easy. The below command creates a new virtual filesystem called testB using the EAPATH to the existing EA index.

$ ferris-create-fca-tree --index-path=EAPATH --treename=testB \
small_size '(size<=100)' \
... \
gnome_nameof '(url=~.*gno.*)'

In the above command you are creating a virtual filesystem tree and specifying which attributes you are most interested in. You can have as many attributes as your machine specification allows (start with 10 or so and play around). The attributes are in the form of SQL_NAME ffilter. Where SQL_NAME has to pass as a SQL column name and the ffilter is a standard ferris filter/search string that you would use in feaindexquery.

Once the above command has completed you can start using your new FCA filesystem with anything that takes advantage of libferris, for example, ferrisls. The FCA filesystems are exposed using the PostgreSQL database name as part of their path. So the below assumes that pgfoodb is the backing database that the EA index at EAPATH is using. The minus -0 option is like -l but tells ferrisls to show the metadata that the filesystem itself thinks is interesting instead of the metadata like protection, mtime, owner, group etc.

$ ferrisls -0 fca://localhost/pgfoodb/testB

In the directory you'll find subdirectories and two special subdirs "-all" and "-self" which show you the files that match the query for the current directory. The difference is that "-all" shows you files which might match the queries for some subdirectories, whereas "-self" shows only the files that do not match any subdirectories.

The ego file manager also supports using the FCA virtual filesystems. The "Ext" column shows the number of files which match any given directory. See the FCA sidepanel in action.

5. Its crashing or giving incorrect behavior

5.1. When I read a directory with libferris it locks the CPU at 100% and doesn't seem to get anywhere after a very long time.

If you have logging set to DEBUG for many things then this can make things take a very long time even though things are progressing. Use ferris-capplet-logging to set the logging to none for all by setting the all slider to none. Try the same thing again to see if you were just logging a lot of information and getting slow response.

Some versions of gamin such as 0.1.7 are known to cause problems with libferris. If you are getting libferris clients eating 100% CPU when reading kernel (file://) or NFS directories then update to 0.1.8-3 from Fedora Development to resolve this issue.

5.2. I'm getting SEGV when any client app closes!

Have you made sure that only one version of Berkeley db (aka sleepycat db, aka db-4.0, db-4.1) is linked?

For the most part libferris itself tries to use libstldb4 which itself includes a version of db4.1 which has a unique name and will not conflict with other versions.

As at 1.1.12 both the redland RDF library and sleepycat's dbxml use berkeley db. dbxml requires db-4.1 and redland can use anything from version 1 to 4.1 (though as this is being written the earlier version support may not be around much longer). There can be many versions of db in the same app as long as each version of db has been compiled with a different --use-uniquename which the current (Oct 2003) dbxml can not do. The easiest thing to do is to configure your redland to use the db-4.1 that your dbxml is using. This can be done by downloading the redland src.rpm file, rpm2cpio'ing it modifying the specfile and rpmbuilding redland again.

mkdir -p /tmp/redland-dbx
cd /tmp/redland-dbx
rpm2cpio /whereever/redland-0.9.13-3.src.rpm |cpio -id
# edit redland.spec to include this with your adjusted paths in its configure line
--with-bdb=/usr/local/dbxml/db4 --with-bdb-lib=/usr/local/dbxml/db4/lib
--with-bdb-include=/usr/local/dbxml/db4/include --with-bdb-dbname=db-4.1
# rebuild redland from modified specfile.

See also redland building directions.

5.3. The clients all seemed very nice but all of a sudden many clients started crashing before doing anything interesting. How to fix it?

I noticed that sometimes the db files which redland uses to store your personal RDF can become corrupted. This mainly happens when libferris is changing your personal RDF and you kill the client while it is doing this. Unfortunately once the db files are corrupt you'll just notice things that used to work fine start giving you segvs.

Check in ~/.ferris/rdfdb using db_verify that your db files are still OK. See this FAQ for how to move to using another redland backend for your RDF store such as Sqlite.

5.4. I am having problems mounting PostgreSQL with libferris.

There has been a report that the hierarchical extensions in PostgreSQL don't work with the database mounting in libferris. The below error message was reported with hierarchical extensions problems.

read() e:ERROR: unexpected right parenthesis

read() e:ERROR: unexpected right parenthesis

read() e:ERROR: unexpected right parenthesis

ls.cpp cought:NoSuchSubContext, 836: Resolver.cpp virtual
Ferris::fh_context Ferris::RootContextFactory::priv_resolveContext(
Ferris::fh_context, const std::string&amp;, const std::string&amp;)
Should never happen! rdn://localhost/tmp/foo rest:tmp/foo
For path:/localhost

6. Implementation choices

6.1. What is the point of this user address space virtual filesystem as apposed to traditional in kernel stuff?

Firstly, its not strange for a filesystem to not live in the kernel. Many micro kernel based operating systems have their filesystem as a seperate compontent from the kernel.

By making the filesystem run in the user address space libferris can use shared libraries to provide its filesystem interface. For example, there is little change of an XML parser getting into the mainline Linux kernel and if it did then it would surely be a limited XML parser. Having libferris not in the kernel gives access to XML parsers, ODBC drivers and many other libraries that are not candidates for kernel inclusion.

6.2. So how can I get at libferris from legacy apps?

There are two main styles. Either using a LD_PRELOAD hack to override libc functions and switch between the original function and using libferris in the new trampoline function or by exposing libferris as an NFS or CODA server and having the kernel mount it.

There are already a great deal of projects around that allow one to expose a user address space virtual filesystem to legacy applications should you require this. See this part of my links for starters.

6.3. Why did you use C++ instead of C, Java or whatever?

ANSI C is a rather nasty choice because it lacks nice generic containers, clean stream abstractions, and even a basic string class. It seems that there are some who "ewww" at libferris because its coded in C++ but they can always just use gnome-vfs if language choice is their primary decision point.

6.4. Your using libX for your X plugin but I like to use libY to look at that kind of data. Any chance of changing?

If you use a different library to access it then your best bet is to copy the existing plugin into a new dir and modify it to use your favourate library. Patches for conditional build will most likely be accepted.

An example of this would be to make a libxml2 plugin for mounting XML data instead of using xerces-c.

6.5. How does ferris-out-of-proc-notification-deamon communicate?

Using named pipes. A named pipe is created by the daemon and data that is read from that pipe is sent to all clients but the one that wrote the message.

6.6. What is sent to other libferris applications by ferris-out-of-proc-notification-deamon?

Information about what emblems are attached to a file, changes made to the contents of various filesystems such as db4.

Using this a GUI client can just watch a path within a db4 file and see the changes that a command line tool makes to the contents of that db4 file. This was originally added so that filesystems like apps:// can be links to db4 files in ~/.ferris but clients are not effected by this choice, ie. clients are independent of how apps:// is stored they just respond to "created" events and don't care how they are generated.

6.7. Are you supporting the shared VFS standard?

As of Sep 2003 there is a thread on the freedesktop xdg mailing list about having a standard interface for both gnome-vfs and KDE's kio. I will most probably include support in libferris for the freedesktop standard when (A) the design of the shard VFS is complete (B) I have the time.

Note that such support will probably not unlock all the flavor of libferris simply because there are features in libferris that are not available in KDE/GNOME.

6.8. Since libferris uses some Boost code, why does libferrisstreams exist instead of just using boost::iostreams?

The short answer is that ferrisstreams predates boost::iostreams. The 0.2.0 release of ferrisstreams was made in May 2003. The ferrisstreams code itself came into existance from the Streams.hh and Streams.cpp files in earlier libferris releases.

Also now that boost::iostreams exists there are probably different design goals for it and ferrisstreams. That said, you can happily use both libraries together :) The below is a little snip of code showing just this sort of thing.

namespace io = boost::iostreams;
using namespace boost;


io::filtering_istream in;

regex pattern( "foo(.*)bar" );
in.push(io::regex_filter(pattern, "ZZZ" ));

// push a libferris stream on as the data source.
fh_ifstream iss("my_file.txt");
in.push( iss );

copy( istreambuf_iterator<char>(in),

The code in ferrisstreams will probably remain seperate from boost::iostreams. Some functionality that boost::iostreams has will likely even be reimplemented in ferrisstreams, for example, recently a TeeOStream was added to ferrisstreams.

6.9. Are there any wrappers for perl, python, php, mono, java, scheme?

As at 1.1.11 a reasonable SWIG interface file was created. At the time wrappers for both perl and python were integrated into the build environment. Pass --with-swig-perl to generate the perl bindings and for python use the following two --with-swig-python-version=python2.2 --with-swig-python-prefix=/usr for ./configure

The main challenge left in the wrapper is to try to expose the glory of IOStreams to perl/python. The smart pointers and other STL type stuff is already handled.

I might add in some more languages which should be reasonably easy to do now that the main SWIG interface file is created.

7. General questions

7.1. It seems to run very very slowly sometimes (or always).

Run the ferris-capplet-logging client and make sure you have your logging levels set to something reasonable. Grabbing the "all" slider and moving it to None will set everything to do no logging. Something like Notice or less for things should be a nice choice.

7.2. How can I edit a file from a libferris virtual filesystem using my current text editor (libferris version 1.1.80+).

There are many ways, ferris-file-to-fifo is the simplest way to edit a file from a libferris filesystem in an editor which is capable of editing files from fifos (eg. vim). The more advanced answer is to use the Samba VFS module to export the interesting part of your filesystem to Samba and then use the kernel's ability to mount Samba to expose your libferris to standard applications.

The below shows how to edit part of a tuple from a PostgreSQL database using vi. If someone knows the magic to make (x)emacs happily edit a fifo and write the update in place then drop me a line and I'll add info here. Any client that follows the read-it-all, do something, write-it-all back will work with ferris-file-to-fifo. Use ferris-redirect if you just want to write data to a file and fcat to get just the reading part.

=# create database play;
play=# \c play;
play=# create table msgs ( id serial primary key, msg varchar(1024) );
play=# insert into msgs values ( default, 'hello there' );
play=# \q
bash$ vi $( ferris-file-to-fifo --ea msg pg://localhost/play/msgs/1 );
# modify the varchar data and ZZ to update database.

7.3. Can I bind the default view and edit action for a file based on arbitrary metadata (libferris version 1.1.81+).

The usual system is to determine what action to take for view and edit operations for a file based on the Mimetype of that file. In libferris 1.1.81 this was extended to allow for the action to be determined by any metadata about the file. For example, you might wish to stretch video which is in 4:3 aspect ratio to fill your 16:10 monitor. The below gives an example of how to setup this sort of binding. As you can see from the example the selection of action is based on an arbitrary matching predicate for the file. Using ferrisls with the --show-ea option is quite handy for determining what your predicates should be.

$ fmkdir -p mime://filtered-bindings
$ fmkdir -p mime://filtered-bindings/box-video
$ echo '(&(is-animation-object==1)(name-extension==avi)(aspect-ratio<=1.6))' \
| ferris-redirect -T mime://filtered-bindings/box-video/ffilter

Once the above is run you will need to link the application which is run for view and edit in some manner. The simplest way to do this is to right click on a file which matches the above predicate in ego and use the "Edit Actions" menu to select the application you wish to associate with files matching the predicate.

Another possibility is to use emblems to explicitly select the default applications for files. If you have a 1610 emblem which you attach to files you want to override to be shown in 16:10 with scaling then you can use a simple predicate such as (emblem:has-1610==1) as the ffilter in the above example and decide which files to scale by default by attaching this emblem to those files.

7.4. Is there a top level filesystem that controls them all?

You can step into other filesystems from the root:// filesystem. The paths and URLs become a little strange when you do this because the filesystems you are stepping into expect to be top level schemes. For example the file:// URL handler expects something at path /var to be able to be lstat()ed which is not the case using its root:// path of root://file/var.

This restriction means that root level URLs are not usable as round trip URLs. A root://file/whatever will always give the same file but if its read that files URL then you will likely get x-ferris://whatever insead of its root:// level URL.

One of the main motivations for the root:// scheme is for query schemes to build on. For an example of this see the XPath point in this FAQ.

7.5. How can I find out what context plugins I have?

The best way is with the context:// filesystem. You can see some of them listed in the root:// filesystem too. Be aware that contexts that only support being mounted over other contexts will not appear in root://. For example, db4 and xml plugins are only listed in context:// because they both require the presence of another filesystem to work properly (ie. the read bytes from a file and turn that into a filesystem for you).

$ ferrisls context://

7.6. How can I get the agents to work?

In the build there should be a capplet at cc/capplets/agents/ferris-capplet-agents which will let you setup your agents. As of 1.1.10 there are only binary classification agents, this means that the agent will be able to train on the attachment of one emblem to files and then offer a guess as to if a new file should have that emblem or not. Use the capplet to create an agent, give it a statedir someplace in your home directory and tell it what implemenation to use, I recommend svm_light. You tell the agent what emblem to train on and use for predictions in the capplet too.

Training and predictions are doing using apps/ai/fagent. First you should train an agent using a bunch of files that you have manually tagged with an emblem (use fmedallion to tag files from the command line). Then for the agent you will have to train it with the following.

$ fagent -t -a myagent list-of-files

After the agent is trained you can have it express its optinion by running the following

$ fagent -a myagent list-of-files

myagent is the agent's name that you assigned in the capplet when creating new agents. Each agent must perform its own training and should have a unique statedir.

7.7. What is the format for time strings?

As of libferris 1.1.31 the time string formats were updated and enhanced. A time string now consists of two major parts, an absolute time and a relative time. Both parts are optional though must occur in that order if they are both present. If the absolute time is not present then the current time is used as the absolute time, for example, "+ 1 month" will give you a time that is a month from whenever it is used.

An aside for the advanced users: the code now uses a Recursive Decent Parser provided by the Spirit part of the Boost library. The Spirit parser is defined with a syntax that is close to BNF, the source is in Ferris/General.cpp which will give you a complete idea of the syntax.

An absolute part is the definition of time based on a fixed point. Extensions are included so that the absolute time can be taken from a time EA in a file, for example, you can set the absolute time to be the modification time of a file.

In the below examples, tests/timeparsing/timeparse takes as the first argument a time string that libferris can understand and prints out the time now (relv) and the resulting time from the provided time string (tt). The getea function can be used to read a time value from another file as the absolute time, there are mtime and atime functions which get the modification and access time for the supplied URL. For mtime and atime you don't have to use parenthesis but when they are omitted then you must quote the URL with either matching single or double quotes.

$ cd ./tests/timeparsing/
$ ls -l timeparse.cpp
-rw-r----- 1 foo bar 3435 Sep 28 16:17 timeparse.cpp
$ ./timeparse 'mtime "timeparse.cpp"'
Input:mtime "timeparse.cpp" tt:1096352223
Output:04 Sep 28 16:17

$ ./timeparse '2004 feb 15'
Input:2004 feb 15 tt:1076767200
Output:04 Feb 15 00:00

$ ./timeparse '2002/mar/30'
Input:2002/mar/30 tt:1017410400
Output:02 Mar 30 00:00

$ ./timeparse '2002/3/30'
Input:2002/3/30 tt:1017469405
Output:02 Mar 30 16:23

$ ./timeparse '5/30'
Input:5/30 tt:1085898229
Output:04 May 30 16:23

$ ./timeparse 'getea(timeparse.cpp,atime)'
Input:getea(timeparse.cpp,atime) tt:1098026697
Output:04 Oct 18 01:24

# only 0X dates are accepted as two digit.
$ ./timeparse '05-12-25'
Input:05-12-25 tt:1135491904
Output:05 Dec 25 16:25

Relative times allow you to move the absolute time by a positive or negative offset in a unit of time. Such offsets can be chained together, for example, "+1month -3 days" moves by a month and back 3 days. The plus and minus signs are optional in the past example. You can also use "ago" as a postfix operator, for example, "3 days ago" will do what you think.

Yesterday, today, and weekdays are also valid inputs. Be aware the weekdays are for the current week, so if its Thursday then Friday will return tomorrow.

There are last/next and begin/end last/next prefixes. For example, "begin last week" will return the time when last week started. Most strings have both the full version like "beginning of next" and the short version like "bnext".

$ date
Wed Oct 20 16:25:23 EST 2004

$ ./timeparse 'bnext month'
Input:bnext month tt:1099231200
Output:04 Nov 1 00:00

$ ./timeparse 'beginning of next month'
Input:beginning of next month tt:1099231200
Output:04 Nov 1 00:00

$ ./timeparse 'blast month + 4 days'
Input:blast month + 4 days tt:1094306400
Output:04 Sep 5 00:00

$ ./timeparse 'elast month 5 days'
Input:elast month 5 days tt:1096984799
Output:04 Oct 5 23:59

$ ./timeparse '+13 days'
Input:+13 days tt:1099376769
Output:04 Nov 2 16:26

$ ./timeparse '+13 days+2months'
Input:+13 days+2months tt:1104647179
Output:05 Jan 2 16:26

$ ./timeparse 'enext month-2days'
Input:enext month-2days tt:1101650399
Output:04 Nov 28 23:59

And a final example of the combination of absolute and relative time.

$ date
Wed Oct 20 16:25:23 EST 2004

$ ./timeparse '2004-nov-13 + 2 days - 1 month'
Input:2004-nov-13 + 2 days - 1 month tt:1097762400
Output:04 Oct 15 00:00

$ ./timeparse 'mtime "/var" - 1 month'
Input:mtime "/var" - 1 month tt:1090317520
Output:04 Jul 20 19:58

7.8. Is there a FUSE ( module for mounting libferris through the kernel?

As of libferris 1.1.93 there is a ferrisfuse 0.0.1 module which allows you to export your libferris filesystem to fuse.

The three major things that you should think of feeding to the ferrisfuse module are: the libferris URL to expose, the mountpoint to access this filesystem through the kernel with and a regex of what to force to be seen as a file.

The regex is there because libferris doesn't force a distinction between a file and a directory unless it really needs to. Consider an XML file, if you fcat this file then it appears like a file with byte contents, if you ferrisls it then you will see a virtual filesystem which exposes the structure of the XML file. You can use the regex to force the ferrisfuse module to say this is a file even though libferris can potentially treat it like a directory as well.

An example usage would be:

ferrisfs --url file://tmp \
--force-to-file-regex=".*\.xml" \

If you wanted to expose an xsltfs:// manipulation which creates a virtual (and updatable) office document from a mounted PostgreSQL table you could do the following...

# use the msgs table in the play database
$ ferris-filesystem-to-xsltfs-sheets \
--plugin excel2003 \
'postgresql://localhost/play/msgs' \
--fuse test1

# this script and mountpoint dir were created by the above command
$ cd ~/ferrisfuse
$ ./
$ cat test1/msgs.xml
$ ...
$ ooffice test1/msgs.xml
# update the file in OpenOffice and 'save' it to update your database.

7.9. What is this xsltfs?

With xsltfs you can transform an input virtual filesystem into an output virtual filesystem using XSLT. Note that editing in the output filesystem can also be reflected in the input filesystem.

For a simple example of this consider that the input filesystem is a mounted PostgreSQL table. Using XSLT we can create a output filesystem which is an office spreadsheet document. If we then edit this document and save it then changes are reflected in the input filesystem. In effect we can edit our database table by updating a virtual office document. See the information in the fuse entry for how to set this up.

You can use the xsltfs on other input sources than PostgreSQL as well. Consider for example editing RDF in OpenOffice through a kernel mounted xsltfs:// using libferris' ability to mount RDF.

8. Using libferris from bash

8.1. How can I present two filesystems and see the diffs as a filesystem?

Assume that you have two versions of the same filesystem under /tmp by the names of the before and after subdirectories. The following command will show you what files were added or removed and for the files that have changed the unidiff will be presented. Note that there should be no line break in the --show-columns parameter. ./ferrisls --show-headings -lh diff://file://tmp/before/file://tmp/after \ --show-columns="name,size-human-readable,is-same,was-created,\ was-deleted,is-same-bytes,unidiff,different-line-count"

Note that the diff:// filesystem can work with any underlying filesystem. For example if you have a MySQL database and a remote replica of it (like a local working database and a version on the Internet) then you can get the diff:// of a table or the result of the same SQL query on both databases.

Another interesting example is checking a remote ftp or http site against a local copy.

8.2. How can I see and modify what emblems are attached to a file?

The command line tool apps/fmedallion/fmedallion. The --help page is rather informative for this. This tool should be installed by default. example: FIXME

8.3. How do I interact with the file clipboard from the shell?

A collection of tools in apps/fileclip/ provide cut, copy, paste, undo and redo support. These should be installed by default. example: FIXME

9. A view from an RDF perspective

9.1. What is implicit tree smushing and how can I take advantage of it? (libferris version 1.1.80+).

At times the same data is available to users given different URLs. For example, this website exists both locally on my hard disk and on Another classic filesystem example is data that is available via NFS from many different mount points.

Running the cc/capplets/rdf/ferris-capplet-rdf client you can setup bundles each of which has a set of regular expressions to match against file URLs. For example, I'll create a foo bundle and set the following regexes with the aim of implicitly unifying the metadata associated with the happy-project in my home directory with the backed up version on the network.


With the above in place the following commands demonstrate that metadata is implicitly unified for the same file in both locations. There is a performance hit the first time metadata that requires unification is sought for a URL. This is implicit tree unification because it is done by libferris for you implicitly, smushing is based on regular expression sets which determine tree paths, and smushing actually links both files to the same base metadata node in your RDF store.

$ cd ~/happy-project
$ touch main.cpp
$ echo bar1 | ferris-redirect -T --ea foo1 main.cpp
$ cd /Network/backedup/home/`ud -un`/happy-project
$ fcat -a foo1 main.cpp

9.2. Can I smush two files metadata together? (libferris version 1.1.80+).

The main client for this is ferris-rdf-smush-new-url. You have two main choices; copy existing metadata from one URL to another, or explicitly bind the two URL's to the same metadata base node. Doing the first will copy the metadata as it is now but preserve any existing metadata in the target URL. Binding the two URLs to the same metadata note means that any changes in the future made to the metadata of either URL will be made to the metadata for the other URL as well.

Note that for ferris-rdf-smush-new-url a file does not have to exist at oldurl but one should at newurl. This is to allow metadata which was "left behind" from a file move that happened outside of libferris to be reconnected to the new file.

$ # just copy metadata which doesn't exist in target
$ # two files still have seperate metadata base nodes.
$ ferris-rdf-smush-new-url oldurl newurl

$ # The below command discards the metadata of newurl completely
$ # the base metadata node of oldurl is then bound to newurl as well
$ # thus future changes to either will be immediately reflected in the other
$ ferris-rdf-smush-new-url --unify oldurl newurl

9.3. Can I run a SPARQL query against myrdf store? (libferris version 1.1.80+).

Yes either put the query in a file and specify it is a file argument or pipe the query to stdin of ferris-myrdf-query.

# A simple SPARQL to find the value of the "foo1" EA for foobar cpp files.
$ cat /tmp/query
PREFIX ferris: <>

SELECT ?uuid ?earl
?earl ferris:uuid ?uuid .
?uuid ferris:out-of-band-ea ?bn .
?bn ferris:foo1 ?eavalue .
regex( str(?earl), "^file:///foobar/.*\.cpp$")

$ cat /tmp/query | ferris-myrdf-query -

9.4. Can I use another storage backend then the Berkeley db that is the default? (libferris version 1.1.81+).

Yes, this is setup through a small group of files in your .ferris/rdfdb directory. I've only tested this against the Sqlite backend though others should also work fine.

# How to switch to using sqlite for storage.
$ cd ~/.ferris/rdfdb/
$ echo sqlite >ferris-storage-name
$ echo ~/.ferris/rdfdb/myrdf >ferris-db-name

There is also a file ferris-db-options which contains the options for the backend you are wanting to use. The last parameter to librdf_new_storage here, ie the line with host and user information should be put into your ferris-db-options if needed.

10. A view from an XML perspective

10.1. Can I just see the filesystem as a Document Object Model?

Yes you can. And whats more its a thin wrapper that is created on demand. This means that only the bits of the DOM that you look at are even created, and when created they still just reference the EA and byte content on disk. There is a somewhat natural mapping from Extended Attributes (EA) to attributes in XML and a files content to element content in XML.

The current thin wrapper for DOM is likely to become faster in future libferris releases as it gets used in more and more XML stuff.

10.2. Can I mount a Document Object Model as a filesystem?

Yes you can. This is currently done in a up front memory heavy mode as the DOM to fs mapping isn't used much by me.

10.3. Can I mount an XML document as a filesystem?

Yes, you can also modify the XML file, add new attributes, elements, rename parts of the internal structure and have it saved back to disk.

You'll also find that many of the command line tools will allow such modifications from scripts already.

10.4. Can I apply an XSLT to a filesystem?

Yes you can. Just mount it as a DOM and apply the stylesheet with xalan-c.

10.5. Can I have one libferris application which has mounted an XML file instantly see changes made to the XML document by another libferris application? (libferris 1.1.82+)

Yes. This feature is enabled by default for some plugins like db4. You have to explicitly turn it on for XML files as of libferris 1.1.82+. This is because journaling on XML files requires both metadata and the data itself to go through the journal. This can be slow depending on how large your data is in your XML file. For XML files which you are changing and don't really care about notifications journaling also brings extra overhead.

To turn on full data and metadata journaling for an XML file simply set the libferris-journal-changes EA to 1 or true for the base XML context. For example, if I have an XML file foo.xml I just run the below command and any updates to a libferris filesystem for foo.xml should notify other libferris clients of these changes.

$ echo 1 | ferris-redirect -T --ea=libferris-journal-changes foo.xml

The default policy of only journaling when explicitly asked for can also be inverted. Note that this will mean ALL XML files will be journaled. This might be OK for you if you are not intending on writing large chunks of data to XML files with libferris. You can do this once for all libferris clients or selectively by setting an environment variable. The below shows examples of these two options.

# all XML file changes are always journaled
$ echo 1 | ferris-redirect -T ~/.ferris/general.db/always-journal-xml

# lets set this shell only for all XML journaling
$ ftouch ./example.xml/root/new1

10.6. Are any interesting bits of libferris available to my XSLT pages?

Many interesting functions from libferris are exposed to XSL pages. Take a look into libferris-releasenum/xsltfunctions/ to see what is exposed, if you require any other libferris functions and you wrap them in a sensible way then patches are likely to be accepted into the mainline.

The fnews RSS aggregator uses libferris functions from XSL pages so if your looking at doing that sort of thing you might wish to see how fnews does it currently.

10.7. What about XPath version 1.0 or 2.0?

There is an xpath:// filesystem that allows selection of nodes via an XPath 1.0/2.0 expression depending on what version of libpathan you built your libferris with.

The filesystem from xpath:// will start with each top level URL scheme and then you will decend into its tree. For example xpath:/file/var/tmp will be the same as /var/tmp in your filesystem using the file:// URL scheme. This allows one to use XPath expressions on http, mysql and ftp protocols aswell as file://.

The keen eyed ferris user will note that the XPath query engine is attached to the root:// filesystem. Performing the following should give you a reasonable idea of how to proceed

$ ferrisls -l --show-columns="name,path,url" root://
file /file root:///file
$ ferrisls -l --show-columns="name,path,url" 'xpath:/*'
file /file root:///file
$ ferrisls -l --show-columns="name,path,url" 'xpath:/file/tmp/fakeroot/*'
test.tar.gz /tmp/fakeroot/test.tar.gz x-ferris:///tmp/fakeroot/test.tar.gz
tmp /tmp/fakeroot/tmp x-ferris:///tmp/fakeroot/tmp
tmp.tar.gz /tmp/fakeroot/tmp.tar.gz x-ferris:///tmp/fakeroot/tmp.tar.gz
$ ferrisls -l --show-columns="name,path,url" 'xpath:/file/etc/*[@size<100]'
... pause ... results ...

10.8. Can I use XPaths with ferriscd?

Yes. Whats more if your XPath expression selects more than one node then the first n-1 nodes are pushed onto the directory stack and then you are cd'ed into the last directory.

Make sure that the ferriscd script is placed in /etc/profile.d and that it is sourced by bash.

cp libferris-release/apps/ferriscd/ferriscd /etc/profile.d/
. /etc/profile.d/ferriscd

Then start using ferriscd instead of 'cd' to tell bash where to go. Currently you are limited to moving into directories that bash will like, which is limited to kernel readable locations like file://. I'll probably move bash itself over to the ferris side of the force sometime soon.

x] $ ferriscd 'xpath:/file/var/*'
yp] $ dirs
/var/yp /var/tmp /var/spool /var/run ...
yp] $ popd
tmp] $ pwd

10.9. Can I use XPaths with other ferris tools?

ferriscd and ferrisls have native support for 'xpath:/' queries in libferris 1.1.6. A partial list of apps that will probably support xpath in the future are ferriscp, ferrismv, ferrisrm, fmkdir, ftouch, fcat, fclipcopy, fclipcut and fcompress.

The problem is that the tool has to be aware that a parameter can expand into many parameters. In classic command line interfacing when a wildcard is presented in an invokation the shell expands the wildcard and passes this expanded version to the command line app as its argv. Since bash doesn't natively support xpath at current then command line apps that assumed a standard interaction model have a problem.

The cleanest solution to this is to patch bash to support xpath expansion directly. This will most likely come to pass when I get the time to hack it.

A stopgap solution is to use a script to invoke your tool and inline a call to ferrisls to resolve the xpath for you. All you need to do is to wrap where the URL is normally passed with something similar to funroll below. Note that it would be more general to use --show-columns="url" but by using the path we allow non ferris tools to access the xpath results.

$ ferrisls -U --ferris-filter="" 'xpath:/*'
dvd file ftp http ipc ldap mysql

# resolve a xpath to a format suitable for inlineing in nonferris tools
funroll() {
ferrisls -U --ferris-filter="" --record-seperator=" " \
--show-columns="path" $1 2>/dev/null

# use fileutils 'ls' on the resolved xpath
$ ls -ld $(funroll 'xpath:/file/tmp/fakeroot/*[@size<300]')

10.10. Is there anything like XLink in libferris?

You can use emblems and their partial ordering to obtain n-ary linking in libferris. This works by considering how one would have XLink like stuff in RDF and then thinking of emblem association as triples: File has-emblem emblemName. The linking only really works effectively when you add the files that are linked into the attribute index so that you can resolve links quickly.

Hopefully a formal paper will be coming out sometime early next year explaining this in more detail.

10.11. Why should I be interested in mounting my sleepycat dbxml files?

Once you mount them you can populate, modify and list them just like any other regular filesystem or XML source. Other than the admin gain, your applications can continue to use a nice STL iterator and IOStream interface while saving their data into an indexed XML store. Get 1.1.11 or later and you can mount dbxml today :)

11. The ego filemanager

11.1. Can I setup the order of the columns in the GTK Treeview to something else? (ego 0.12.2)

You set a partial order of column names and the current directory is run against this order to produce the final result. The partial order is specified in a db4 value. Columns are listed comma seperated in the order you wish them to appear in the final display, eg, name,foo,size means that name will be shown first and then size. If there is no foo column in your current directory then size will follow name and be just to the right of it in the display.

The below example will make sure that the emblems and treeicon always proceed the name of a file in your display. Unmatched columns will appear to the right of any explicitly ordered columns.

$ cd ~/.ego
$ echo emblem:emblems-pixbuf,treeicon-pixbuf,is-unseen,name, \
| ferris-redirect -T general.db/column-display-partial-order

11.2. Can I change the label used for a particular EA in ego's column headers? (ego 0.12.2)

This is handled using a key=value config setting in your ~/.ego/general.db file. The format is much like that of CGI parameters where each key=value is seperated by and ampersand. Since the is-unseen EA is binary it is nice to have a shorter column name for it so that column can be nice and narrow. Also the more verbose EA of size-human-readable is nice to know the content of the EA but it is also nice to shorten that down for normal display in ego.

$ cd ~/.ego
$ echo 'is-unseen=F&size-human-readable=sizeh&mtime-display=mtime&protection-ls=prot' \
| ferris-redirect -T general.db/column-rename-mapping

12. Some light hearted questions

12.1. When is the animal book on this coming out?

Um, ar, well... sometime after ORA commision me to work on it :)

12.2. I think libferris and ego are so great I want to make a donation. How can I do that?

If its a small donation using the donation system is fine. I'm also happy to have companies directly sponsor additions to libferris if there is mutual interest.

12.3. Why are you writing libferris and ego?

It won't write itself!

Well... seriously, because I like the topic enough to spend the time.