Affichage des articles dont le libellé est english. Afficher tous les articles
Affichage des articles dont le libellé est english. Afficher tous les articles

dimanche 11 septembre 2011

spacef

Some time ago, I started working on a gopher server (gopher is the protocol behind the gopherspace, an important part of Internet) and I choose to write it in a modern, efficient language: C. The gopher protocol is mostly about sending tab-separated data from the server to the client in response to the requested path. So, most of the parsing work is done on the client side and the server just have to generate this data.

But recent implementations of gopher serves introduced a new concept: Gophermaps: those are server side files that describe what should be displayed to the client. That also means the server has some parsing work to do. But it should be ok, C comes with maybe one the best string manipulation library ever made with short, descriptive function names (for instance, strpbrk or strrchr) and a sane way to store string length (I really wonder why would anyone use an alternate implementation such as bstring).

So, the problem was quite simple: I wanted to read several pieces of non-tab data separated by tabs and wrote something like that:
sscanf(buffer, "%s\t%s", &first, &second);
I expected it would read some non-space data (yes, there's already a problem here if there's space before the tab), then a tab, then some other non-space data. It doesn't.

After reading the man page and doing some tests, I understood a few things about my expression:
  • %s first skips leading spaces, before reading non-space data;
  • \t match any number of spaces, in fact any space in a pattern match any number of spaces.
So my scanf call was the same as this one:
sscanf(buffer, "%s%s", &first, &second);
and is equivalent to this regular expresssion:
"[:space:]*([^:space:]+)[:space:]*([^:space:]+)"
not really what I wanted to read...

First I needed to figure how to read non-tab data only, and it happens that this part was simple enough. The square brackets in scanf patterns work somehow the same than in regular expression. So %[^\t] will read a sequence of non-tab characters (ho yeah... a \t between square brackets only match a tab).

Next I have to read the "only one tab" part and this part was a lot more fun. %[\t] would match a sequence of tabs. To match only a specified number of tabs, you have to use a decimal between the % and the square bracket. The pattern is now %1[\t] but there still a problem: each conversion specification (those %... things) needs to be stored in some output variable and that would be stupid to store a tab each time I need to read one. Scanf provides the * modifier that tells to discard output of the conversion specification. The final pattern for reading a that is then %*1[\t].

And the correct version  of my scanf is:
sscanf(buffer, "%[^\t]%*1[\t]%[^\t]", &first, &second);
For those curious about my gopher server, the mercurial repository is here: http://hg.tuxfamily.org/mercurialroot/gophrier/gophrier/ and there's a mirror here: https://bitbucket.org/guillaume/gophrier

mercredi 26 mai 2010

All your language are belong to mini18n

Mini18n is a tiny i18n library that was original designed to handle translations in Yabause and that is now available on some Linux distribution (well... at least Debian and Ubuntu) as a dependency of the emulator.

When I started working on translation support for Yabause, the obvious solution, as a Linux user, would have been to use gettext. But this solution had a number of shortcomings:

  • gettext is painful to include/maintain in a project: we're using it to translate the glade port of DeSmuME and I still don't understand how it works and/or how to fix it when it
    breaks;

  • we needed a portable solution, something that could be easily built on different platforms Yabause supports;

  • we also wanted to be able to staticaly link it into Yabause binary, so we didn't had to ship some extra DLL.

(btw... I can be wrong on some/all of these points, I'm not a gettext expert)

So I ended up writing a small translation library and make it live in the Yabause CVS, so every Yabause developer could fix it, if he needed to.

This was two years ago, and not much changed since that time: CVS is now a SVN ; Gtk+, Qt and Windows ports of Yabause can use mini18n for their translations ; I also started to work on gettext format support for mini18n but didn't finished it; all in all, there wasn't much interest in the library until recently.

Some friend asked me if mini18n could be updated to include multiple language support... at once. He's working on some server software and thought about using mini18n to translate
strings sent to the client. Problem is if you got some french, german and italian users at once, you only have two choices: fork a process for each different language or load those three languages in the same process with a multi-language library.

After taking a quick look at mini18n codebase (yeah... it was a while I didn't worked on it...), I realized it wouldn't be hard to add the feature. On the other hand, the project sounded quite fun and would double the number of programs using mini18n! In fact, the change took me longer than I expected, mostly because I wanted to keep old single-language interface intact and also the two interfaces to play nicely with each other. So, mini18n has now a new API and the old API was rewrote on top on the new one. There shouldn't be problems for a program to use both APIs at the same time.

Next step will be to make sure I didn't broke ABI compatibilty with previous release and if that's ok, we may see another mini18n release soon!