chris blogs: March 2005

30mar2005 · Anarchaia

I’ve been starting another blog—complementary to chris blogs—, Anarchaia. Maybe someday I’ll join both, but for now I feel better if the “weird content” sticks on a different site. Maybe I’ll mirror things that fit to both, I haven’t decided that yet. Subscribe to both. :-)

Interesting is how Anarchaia’s blog entries work, I use Vooly to augment the HTML (think of augmented reality), to insert common patterns. A post on how that works will follow.

As a side note, I’m in Munich till Saturday evening, so do not expect me to read mail or update any blog.

NP: Suicide Commando—Acid Bath

30mar2005 · The T Object-System (in Ruby)

The T programming language is a dialect of Scheme developed during the 1980’s at Yale university. In it’s manual, it says:

T is a dialect of Lisp derived from Scheme. It is comparable in power and expressivness to other recent Lisp dialects such as Lisp Machine Lisp, Common Lisp, and NIL, but fundamentally more similar in spirit to Scheme than these other Lisp dialects.

T is an attempt to create a powerful and expressive language which is portable, efficient, and suitable for a broad variety of applications, including symbolic, numerical, and systems programming.

T draws from many sources in its attempt to provide a complete yet simple and unified system. For the most part, the language’s designers are collators, not originators. Common Lisp, NIL, and Scheme have been the strongest influences.

The manual contains a lot of interesting stuff, the one I found most interesting however is T’s object-system. This object system is so easy (one could say trivial), but still so powerful and flexible that you just need to admire it.

Basically, there are two procedures to implement the T object-system, OBJECT and OPERATION. OBJECT takes two parameters, a value proc that gets called if the object itself gets called (I leave that out in the Ruby version, since it only makes sense with Lisp-1s and rarely has an use anyway), and a list of handlers, each element consisting of an symbol and an lambda. OBJECT returns a new object that responds to the handlers given.

In my Ruby re-implementation, we would write:

# An object responding to `op'
obj = TObject.new(:op => lambda { |s| 34 })
obj.op  # => 34

The first parameter of the lambda is used to pass self, but you can pass several arguments, of course:

# An object responding to `op'
obj2 = TObject.new(:op => lambda { |s, x| x })
obj2.op(55)  # => 55

As you can see, we already have polymorphism. So far, this is not very spectacular, though. The really interesting things about the T object-system are operations and joins.

T operations are like methods of Object in Ruby. They are used as a default everywhere except you did override them (In my Ruby reimplementation, operations are only defined for TObjects, not for every datatype as in T):

# An operation to do `op'
TObject.operation(:op) { |s|
  :zebu         # By default, return :zebu
}

TObject.new(:op => lambda { |s| :quagga }).op  # => :quagga
TObject.new({}).op                             # => :zebu

So far, the object-system is rather boring, there is no real instantiation, no mixins, no singleton classes, all the cool stuff from Ruby is lacking. :-) Enter JOIN, it takes an unlimited number of objects and combines their behavior:

foo = TObject.new(:foo => lambda { |s| :foo })
bar = TObject.new(:bar => lambda { |s| :bar })

joined = TObject.join(foo, bar)

joined.foo          # => :foo
joined.bar          # => :bar

Now, I can’t think of anything (except method_missing, which easily could be handled as a special-case) that’s possible in Ruby’s object-system, but not T’s.

(You may argue that the T object-system lacks instance variables, but those can easily be replaced with accessable closures, which both T and Ruby have.)

Another nice thing about the T object-system is that it’s soo easy to implement (20 lines of Ruby), that you could quickly write it in low-level lanuages as C as a foundation for a future programming language.

By the way, here is the T object-system in Ruby:

class TObject
  attr_reader :handlers
  @@operations = {}

  def self.operation(name, &block)
    @@operations[name] = block
  end

  def self.join(*args)
    TObject.new(args.inject({}) { |a, e| a.merge e.handlers })
  end

  def initialize(handlers)
    @handlers = handlers
  end

  def method_missing(name, *args)
    (@handlers[name] or @@operations[name]).call self, *args
  end
end

NP: Interpol—Next Exit

28mar2005 · Best Of Quotes

Das Beste der besten Quotes von März 2004 bis März 2005:

Lehrer:

I want to fuck you! I want to fuck you!

Wenigstens seid ihr witzig… es gibt auch dumme Leute, die nicht witzig sind!

Man hat das ja gesehen, im 3. Reich mit der Zivilcourage — die hatten ja alle den Schwanz im Arsch. [meint: eingezogen.]

Ich war Ministrant, Oberministrant natürlich. Willkommen in der Gosse!

do re mi fa so le mi o [die italienische Tonleiter]

… und als das mit den KZs bekannt wurde, war natürlich der Ofen aus mit der Freundlichkeit.

Kompetzen (was haben wir gelacht…)

Wie heißt ihr Friseur?Du siehst aber auch nicht besser aus, oder?

Glotzt nicht so romantisch!

Schüler:

Hat’s schon geklingelt?Keine Ahnung. In meinem Kopf klingelt’s ständig!

Du warst doch nicht da, als der Jesus gekreuzigt wurde!Ja zum Glück! Sonst hätt’ ich noch drauf geschossen!

Und ich, hacke Zug aus’m dicht.

I love you. [Zum Englischlehrer als Demonstration von Ironie.]

Ich bin genau um halb zehn geboren!Pünktlich zu Knoppers!

Die ist schwanger!Toll!Nee, gar nicht! Was meinsch’ wie die danach aussieht!

Guck mal! Bert hat ‘ne Gehhilfe mit Licht! [Herr Körner schiebt einen Tageslichtprojektor vor sich…]

Ich bin ein Kamasutrababe, bums mich ins Koma!

He did studies in “black holes.”

He, Frau Riggenbach, schön braun sind sie geworden!Ond an mords Sonnabrand aufm Zinka!

Wer meint, das was fehlt, darf mir gern seine eigene Zusammenstellung mailen.

NP: Pearl Jam—Elderly Woman Behind The Counter

26mar2005 · One Year of "chris blogs"

Today, exactly one year ago, I started this blog. Actually, I didn’t expect I could keep it current for a that long time. (My former blog, started in January 2003 died before March 2003). But, as you see, somehow I managed it, though.

Maybe it’s time for a few interesting facts:

  • The 5 most played artists were:

    41 Bob Dylan
    26 Pearl Jam
    20 Dan Bern
    07 Die toten Hosen
    06 Interpol

  • The raw entries are 1.1 megabytes in 264 files.

  • The blogging tool I used at the very beginning was 127 lines of Ruby code. When I wanted monthly archives, the thing broke and had 166 lines of Ruby. Nukumi2 as of now ~2000 LoC and 400 lines of templates. It worked better than everything before, but I still have new ideas all the time. :-)

  • I posted about 471 quotes directly taken out of real-life. A Best-Of will follow.

I wish you happy Easter or whatever you happen to celebrate on the first sunday after the first fullmoon after the vernal equinox.

Now, on to the next year of blogging!

NP: Pearl Jam—State Of Love And Trust

24mar2005 · Byrne's Euclid

Paul Graham’s last second to last essay, “Undergraduation” contains as a footnote a link to Byrne’s Euclid,

[a]n unusual and attractive edition of Euclid […] published in 1847 in England, edited by an otherwise unknown mathematician named Oliver Byrne. It covers the first 6 books of Euclid’s Elements of Geometry, which range through most of elementary plane geometry and the theory of proportions. What distinguishes Byrne’s edition is that he attempts to present Euclid’s proofs in terms of pictures, using as little text - and in particular as few labels - as possible. What makes the book especially striking is his use of colour. […]

It really looks like an excellent book, especially if you are easily confused by short identifiers (that is, one char), possibly indexed by another char. (I get reminded of Knuth’s “The Art Of Computer Programming”…)

Still, the use of no identifiers at all, but graphics everywhere can get pretty confusing, too.

NP: Dan Bern—Too Late To Die Young

22mar2005 · Vooly

This is one of these ideas I wished I had about a century ago, because the “world” would have been much better then. I’m talking about my new metalanguage. “Oh, no!”, I hear you scream… “Not another metalanguage! Isn’t it enough we already have XML and YAML and OGML and RLML?”

Yes, you have. And they solve their job. XML is a ok format for writing websites and manuals, but it sucks for RPC, for example. YAML is a nice format for config files, but I don’t like indenting all my text. Every metalanguage was made with a reason, and that (at least, should) be the part where it’s really good at. For the other parts, most languages suck.

Vooly surely is no difference here. It was made by me to allow for expressing hierarchical, as free-form as possible data with low cost of markup (almost no escaping), userfriendlyness (you can learn the language in 2 minutes, I promise) and extremely (not to say trivial) implementation. I already wrote two implementations of it in Ruby, one Push-API and the same again for pulling. Both are about 30 lines of pretty Ruby. :-)

Vooly is soo damn easy, everyone can pick it up in two minutes. A Vooly document is a text file, for example, let’s markup an recipe:

Cuba Libre
- 2 ounces Rum (White)
- 3/4 ounce Lime Juice
- 5 ounces Cola
Put lime juice and a twist of lime into highball glass. Add
rum and fill with cola.

Now, let’s markup the different parts of the document, by surrounding them with << and >>:

<<
  << Cuba Libre >>
  <<
     << 2 ounces Rum (White) >>
     << 3/4 ounce Lime Juice >>
     << 5 ounces Cola >>
  >>
  <<
     << Put lime juice and a twist of lime into highball glass. >>
     << Add rum and fill with cola. >>
  >>
>>

Now, add information about what’s between the tags by writing it directly after the tags where this makes sense:

<<recipe
  <<drink Cuba Libre >>
  <<ingredients
     << 2 ounces Rum (White) >>
     << 3/4 ounce Lime Juice >>
     << 5 ounces Cola >>
  >>
  <<steps
     << Put lime juice and a twist of lime into highball glass. >>
     << Add rum and fill with cola. >>
  >>
>>

That’s it, you’re done! See, Vooly is soo easy. :-)

With this knowledge, you can apply Vooly to just about everything. Make your own Vooly-RSS, Vooly-RFC822, Vooly-DocBook today! (Don’t forget converters to take over the world :-))

Heck, you can even embed XML or YAML if that makes sense to get your job done. There shouldn’t be any escaping problems, simply stuff your things in there. (I need to further specify what happens if you want to insert <<, a rather rare sequence of chars in ordinary text.)

Ruby libraries to do cool stuff with Vooly are soon to come.

NP: Bob Dylan—Tomorrow Is A Long Time

20mar2005 · Various hacking

I worked over the weekend on various things. First, I fixed darcsum.el to strip/convert ANSI character sequeces, since several people complained. Thanks to Jose Antonio Ortega Ruiz and Matthieu Lemerre for reporting this. Please darcs pull to grab the latest changes.

Then, I spent some more time on Kashmir/Elusion and added a “Do What I Mean” mode. For example, instead of

^entries.each{
  ^recent?.true?{NEW}
  ^body
  ^time.with{^day/^month/^year}
}

You can now write (assuming you use Elusion):

^entries{
  ^recent?{NEW}
  ^body
  ^time{^day/^month/^year}
}

Elusion will then figure out on it’s own what you want to do (If there is an #each, iterate. If it’s true, just call the block. Else yield self to the block.) Also, you can now pass a block to Kashmir#expand to automatically create an Elusion.

Now, there is a restartable exception library to be coded…

NP: Bob Dylan—Knockin’ On Heaven’s Door

18mar2005 · Waldsterben & Trockenperiode

Sprüche aus dem WG, wo die Schließzeiten an den Türen kürzer sind als in der Jugendstrafvollzugsanstalt, Mathestunden verwechselt, Zillen gebaut, Kurse gewählt werden und die Hälfte der Klasse in Amerika ist. Trotzdem ist einiges passiert:

I want to fuck you! I want to fuck you!

Was ist ein Altersheimausflug ins Grüne?Waldsterben.

Was ist ein Tropfen Blut in der Wüste?Trockenperiode.

Die Frau wurde [früher] verheiratet. Wir kennen Gegenden, wo das heute noch so ist…Kreuzberg!

Laber nicht!

verschlägern

Jesus wäre über einen Kreuzbandriss froh gewesen.

In einer gewissen Scheisse… äh, Weise.

Die Menschen mussten arbeiten bis sie starben. Nur um zu überleben.

Konjunktiv Futur 2½

Loch Ness ist ‘ne Schlampe.

Identitätsbildung in der Jugend: Ausweise fälschen.

Ausser Konkurrenz:

Ah, ich bin stolz darauf wenig gegessen zu haben.

Das hat mir jetzt aber garnicht geschmeckt.

NP: Dan Bern—Jail

15mar2005 · Restartable Exceptions

One thing I always liked about Common Lisp is its mighty condition system. Conditions provide what Exceptions do in Ruby, and more.

Usually, when an Exception is raised in Ruby, it may be rescued by providing an error handler (possibly even a generic one that displays an error page in a web application, for example), or ignored, which will end the program. One thing you cannot do in Ruby is to use Exceptions to emit warnings, as there is no way to resume the erroneous code when you are already in the handler.

Not till now, at least. :-) I quickly coded an Common Lispish system to restart exceptions. It’s best shown by developing a simple application, so I’ll port the piece of code that explains Conditions in Practical Common Lisp.

This is an interactive Ruby description. Fire up your irb and paste the very last piece of code. Now, please copy and paste the code as you are reading to try the examples live. Happy hacking.

First, here is the code without any error handling:

class LogAnalyzer
  def initialize(logs)
    @logs = logs
  end

  def analyze
    @logs.each { |log|
      analyze_log log
    }
  end

  def analyze_log(log)
    parse_log(log).each { |line|
      p line
    }
  end

  def parse_log(log, &block)
    entries = []
    log.split("\n").each { |line|
      entries << parse_log_entry(line)
    }
    entries
  end

  def parse_log_entry(line)
    line[/^\d+ (\w+)/, 1]
  end
end

Let’s call it with three example logs:

def test
  LogAnalyzer.new([<<LOGA, <<LOGB, <<LOGC]).analyze
1 foo
2 bar
3 quux
LOGA
"?$%
&)%$
-,$"&
4 good_but_to_late
LOGB
1 foo
bar
3 quux
LOGC
end

test

You now should see this output:

"foo"
"bar"
"quux"
nil
nil
nil
"good_but_to_late"
"foo"
nil
"quux"

All those nils are mistakes in our log files! Eww. We should raise an error if there is an erroneous line in our logs, and then ignore it so our program will not totally stop:

class LogAnalyzer
  class MalformedLogEntryError < StandardError; end

  def parse_log(log, &block)
      entries = []
          log.split("\n").each { |line|
      begin
        entries << parse_log_entry(line)
      rescue MalformedLogEntryError
        # Skip
      end
    }
    entries
  end

  def parse_log_entry(line)
    line[/^\d+ (\w+)/, 1] or raise MalformedLogEntryError
  end
end

This will result in (rerun test):

"foo"
"bar"
"quux"
"good_but_to_late"
"foo"
"quux"

Now, this looks much better, but we obviously should tell the user we had to skip some entries. Now, this is where the trouble starts. Usually you would not want to put that notification code into parse_log, because parse_log is really our backend… it should go to analyze. Here, restartable expections become a need:

class LogAnalyzer
  def analyze
    @logs.each { |log|
      begin
        analyze_log log
      rescue MalformedLogEntryError => e
        warn "Skipped invalid line: #{e.data}"
        e.restart :skip_line
      end
    }
  end

  def parse_log(log, &block)
    entries = []
    log.split("\n").each { |line|
      begin
        entries << parse_log_entry(line)
      rescue MalformedLogEntryError => e
        e.data = line
        e.restart_case(:skip_line) {
          # Skip
        }
        e.handle_restarts
      end
    }
    entries
  end
end

Now we get this output:

"foo"
"bar"
"quux"
Skipped invalid line: "?$%
Skipped invalid line: &)%$
Skipped invalid line: -,$"&
"good_but_to_late"
Skipped invalid line: bar
"foo"
"quux"

Ignoring invalid entries is obviously not the only way to handle invalid logfiles (one could say it’s a rather bad way). We may want to replace single lines with something different. Let’s implement this approach, called use_value just like in Common Lisp:

class LogAnalyzer
  def analyze
    @logs.each { |log|
      begin
        analyze_log log
      rescue MalformedLogEntryError => e
        e.restart :use_value, "xyzzy"
      end
    }
  end

  def parse_log(log, &block)
    entries = []
    log.split("\n").each { |line|
      begin
        entries << parse_log_entry(line)
      rescue MalformedLogEntryError => e
        e.data = line
        e.restart_case(:skip_line) {
      # Skip
        }
        e.restart_case(:use_value) { |replacement|
          entries << replacement
        }
        e.handle_restarts
      end
    }
    entries
  end
end

Works beautifully, now all invalid lines get xyzzy:

"foo"
"bar"
"quux"
"xyzzy"
"xyzzy"
"xyzzy"
"good_but_to_late"
"foo"
"xyzzy"
"quux"

To make you see what complex situations you can handle with this simple code, have a look at this example. We silently ignore wrong entries, but when the log has more than 2 errors, we skip the whole thing. Also, lines with bar in it for some reason or another unfortunately got broken, so let’s restore them on the fly:

class LogAnalyzer
  def analyze
    @logs.each { |log|
      invalid_entries = 0

      begin
        analyze_log log
      rescue MalformedLogEntryError => e
        if invalid_entries >= 2
          next
        elsif e.data =~ /bar/
          e.restart :use_value, "BAR"
        else
          invalid_entries += 1
          e.restart :skip_line
        end
      end
    }
  end

  def parse_log(log, &block)
    entries = []
    log.split("\n").each { |line|
      begin
        entries << parse_log_entry(line)
      rescue MalformedLogEntryError => e
        e.data = line
        e.restart_case(:skip_line) {
          # Skip
        }
        e.restart_case(:use_value) { |replacement|
          entries << replacement
        }
        e.handle_restarts
      end
    }
    entries
  end
end

Now, look how the very wrong log totally got skipped:

"foo"
"bar"
"quux"
"foo"
"BAR"
"quux"

Finally, here are the extensions to Exception I did to make this possible (I think I should make that a library):

class Exception
  attr_accessor :data

  def restart_case(strategy, &block)
    @restarts ||= {}
    @restarts[strategy] = block
    self
  end

  def handle_restarts
    callcc { |@continuation| raise self }
  end

  def restart(strategy, *params)
    @continuation.call(@restarts[strategy].call(*params))
  end
end

NP: Bob Dylan—Sara

13mar2005 · darcsum.el polished

Since John Wiegley stopped using darcs, there have been no changes to darcsum.el, a fairly good Emacs-mode for interfacing with darcs.

With the update to darcs 1.0.2 darcsum.el even broke, as the look of the prompts darcs.el waits for has changed. Therefore, I have decided to “fork” it, fix it for 1.0.2 and add features I always wanted in darcsum.el.

You can get the latest darcsum.el by running:

darcs get http://chneukirchen.org/repos/darcsum/

Simply stuff the darcsum.el into your load-path and (require 'darcsum). Run it with M-x darcsum-whatsnew.

Features added by me include a Edit/Update-Log cycle (think of Arch) and having a look at the context of patches (think diff -u).

NP: Silbermond—1000 Fragen

11mar2005 · UUCP revival?

I knew it! UUCP is not dead. Christophe Prévotaux uses it to securely fetch lots of mail.

I think that’s a pretty interesting usecase, escpecially since he tunnels the UUCP over SSH.

I actually thought about using UUCP for scalable peer-to-peer networks, but other ideas like RSS over UUCP (even more useful for podcasts) seem to be pretty good too.

Sean Reifschneider already wrote about Mobile Mail with UUCP back in 2004 already. He did not make use of SSH, though:

While SSH is a good system for doing remote, interactive command execution, UUCP may be better for batched and non-interactive jobs. It’s certainly worth adding to your toolbox of possible network transport options.

Then, did anyone try mail over rsync?

NP: Dan Bern—Edmonton

09mar2005 · 101 Zen Stories

101 Zen Stories contains a lot of interesting and really good zen stories. One however, The Moon Cannot Be Stolen, I like soo much I just gotta have to full-quote it here:

Ryokan, a Zen master, lived the simplest kind of life in a little hut at the foot of a mountain. One evening a thief visited the hut only to discover there was nothing in it to steal.

Ryokan returned and caught him. “You may have come a long way to visit me,” he told the prowler, “and you shoud not return emptyhanded. Please take my clothes as a gift.”

The thief was bewildered. He took the clothes and slunk away.

Ryokan sat naked, watching the moon. “Poor fellow,” he mused, “I wish I could give him this beautiful moon.”

NP: Josh Ritter—You’ve Got The Moon

07mar2005 · Pizza statt Musik

Und mal wieder Quotes aus dem zauberhaften Wieland-Gymnasium, wo Silber in Paprika-Gläsern aufbewahrt wird…

…doch Kondome brauch ich nicht, denn ich spritz dir ins Gesicht!

Meine Nippel brennen!

Die Neurose von Jericho

Ich will mal wissen, wie Kreuzzüge überhaupt fahren können.

Sagen wir, du tötest meine Frau; heute sagt man “Prima!”…

Saufen, fressen und fischen

Wann könnt ihr kommen?So spät wie möglich.

Was ist eigentlich, wenn ein Ritter während der Schlacht scheissen muss?

Die heutige Jugend hat von Wagner lieber Pizza als Musik.

Und für die Programmierer unter uns:

“Statische Sprache” ist ein Euphemismus für Stagnation.

NP: Bob Dylan—Licence To Kill

05mar2005 · Vlerq, or: Thrive for Ruby

I think it’s always a good thing when you have a weird idea, but someone implements it independently quite how you thought of it. That proves your idea was not totally absurd (or you and the other implementor both are… :-)).

Anyway, this happended to me in the case of Vlerq. I have thought of the idea of embedding a small, fast language (I had retroforth in mind) to write quick inner loops for data scanning before. Then, I get to know the people that wrote MetaKit—an embedded database, easy to use and fairly quick (which admittedly is a lot easier to do with embedded databases)—are at writing a fast, embedded Thrill (a dialect of Forth) implementation called Thrive for querying databases. They already have early bindings for Tcl and Python, and I’m right now at coding one for Ruby. The Python binding is less than 250 LOC, so that won’t take too long.

Unfortunately, noone ever did a Ruby binding for MetaKit, but who knows, maybe Vlerq will obsolete it. :-)

The source of Thrive is an excellent example of Huffmann encoding, by the way:

L S cString(P p, I i) { P w = Work(p); I n;
  if (i < 0) i += Length(p);
  n = lenAt(p[i]);
  if (n >= Alloc(p[i].p) || strAt(p[i])[n] != 0) {
    S q = strAt(p[i]);
    p[i].p = newBuffer(w,0,n+1); Length(p[i].p) = n; p[i].i = -1;
    memcpy((M)p[i].p,q,(size_t)n);
  }
  return strAt(p[i]);
}

Whole Thrive is a single ~630 LOC C header (plus some optional extensions) and licensed under the MIT License. ;-)

NP: Dan Bern—Black Boys On Mopeds

03mar2005 · REBOL and Bluemoon

I’ve been thinking of building a syntactically-easy (not to say: trivial), but extremely flexible and extensible programming-language for some months already. I even wrote a sample interpreter for this language, Bluemoon.

Bluemoon is essentially a stack-based, concatenative language quite like postscript, but with reversed words. Instead of the Postscript to calculate, e.g. the BMI (weight / height^2, all code untested):

/weight 60 def   % kg
/height 185 def  % m
/square { dup mul }
weight height square div

you would write this in Bluemoon:

weight: 60  height: 185
square: { :n
  * n n
}
/ * height height weight

This has some beautiful side-effects on thinking in that language, it not only makes your brain unusable for ever doing Postscript (or Forth, Joy or Factor) again, but is actually quite intuitive.

Today, I somehow got the the REBOL page again, and had another look at it. This way probably the first time I looked at REBOL since 2001, or something. I was amazed how similar it was to Bluemoon (rather, the other way round):

weight: 60
height: 185
square: [n] [n * n]
weight / square height

The main difference is due to REBOL’s syntactic sugar for infix operators. And it doesn’t open the stack to you. (Last but not least, Bluemoon is much smaller in it’s design than REBOL, which has 13 core datatypes with unique syntax, even for stuff like “mail address” and “money”.)

Even more interesting are the similarities related to OO: both use prototypes and “contexts” (think of scope).

Unfortunately, REBOL never got popular. IMO, this is related to the lack of a free (as in freedom) reference implementation. It is also the reason why I won’t use it, and rather build my own stuff. Too bad, REBOL really seems to have some nice points (small, extremely portable, easy GUI AFAICS).

On the other hand, having now seen that lots of my ideas were already implemented by others, maybe I should strive for another language design… time will tell. :-)

NP: Dan Bern—Fly Away

01mar2005 · Darcs repositories moved

I finally came around to moving my darcs to chneukirchen.org. The old repositories are still there, but the “Message of the day” will tell you the new location.

As a nice side effect, I also installed darcs.cgi, so you now can browse my source history.

The new repositories reside at http://chneukirchen.org/repos.

Happy hacking. Patches to any projects are welcome. :-)

NP: Bob Dylan—Forever Young

Copyright © 2004–2013