Thursday, December 27, 2007

Christmas Vacation Hacks

Evolution

There's been discussion about Evolution not handling some brokenly rfc2047 encoded message headers recently, so I ported some workaround logic from GMime to Camel yesterday. I think this should turn a lot of frowns upside down when Evolution 2.22 is released. The new rfc2047 header decoder in Evolution is now more accepting than Thunderbird's decoder[1], so I hope this will satisfy everyone.

The new rfc2047 decoder will now handle the following cases:

  • encoded-words embedded inside other tokens (e.g n=?iso-8859-1?q?a=EF?=ve)
  • encoded-words with LWSP in the encoded-text section (e.g. =?iso-8859-1?q?Look ma! there's spaces!?=)
  • some encoded-word tokens with unencoded special chars in the encoded-text section (such as ? marks and other specials - obviously this can't always work if a special sequence occurrs)
  • encoded-word tokens with no LWSP between them
  • no longer aborts if the encoded-text cannot be completely converted to UTF-8 from the charset provided, instead it will attempt to find a more suitable charset to use for conversion and/or replace invalid byte sequences with a '?'
This should fix all of the more common broken-mailer cases, or at least all of the ones that are worth even bothering to try and work around.

GMime

Did some digging and discovered how to document more things using gtk-doc, so GMime's documentation has been getting improved. All of the public functions in GMime now appear in the documentation (I had forgotten to add some of the newer API additions to the gmime-sections.txt file) as well as began writing documentation for each of the doc sections (which I hadn't known how to document previously).

You can find up-to-the-minute GMime documentation here as I continue to chug away at expanding it.

While porting GMime's rfc2047 decoding logic to Evolution, I discovered some areas I could improve in GMime too.

1. After having ported my GMime decoder logic to Camel, I was curious to see how Thunderbird handled broken rfc2047 encoded headers and so had myself a peek at mozilla/netwerk/mime/src/nsMIMEHeaderParamImpl.cpp and noted that my new logic in Camel would handle everything that Thunderbird would handle and more.

I also took a peek at KMail's rfc2047 header decoding logic and discovered Evolution is now more flexible than it as well (again, Evolution now handles everything that KMail's decoder handles and more).

Saturday, December 15, 2007

35 MPG

According to an article in the NY Times, the US Senate has passed a bill to force cars, small trucks and SUVs to achieve an average 35 miles per gallon by the year 2020. Unknown to me, apparently the current bar is at 27.5 mpg for the year 2008, but I can't help but chuckle at that one - perhaps I'm misinformed, but I don't think the average SUV sold today, in 2007, achieves 27.5 mpg or anywhere close to that. Cars, maybe, but not SUVs or small trucks.

Despite my doubts that auto manufacturers will actually adhere to this new standard, lets hope that they at least improve significantly over what they can do today.

In frustrates me to no end seeing so many SUVs on the streets of Cambridge/Boston and the neighboring areas. In my own unscientific estimation, close to 40% of the non-work-related vehicles driving around the city are SUVs and I have to wonder... why?

Is there some off-road driving that needs to be done in the middle of the city that I'm simply not aware of? Maybe they need to drive across the Boston Commons to avoid traffic?

Are these people driving off to the Great Outdoors on weekends and for some reason need an SUV? Like in those SUV commercials where young couples go off and drive through mountain ranges and settle on some cliff to have a picnic while enjoying the view of nature?

If those couples really enjoy nature so much, you'd think that instead of getting an SUV, they'd have bought a (cheaper) car, driven to a parking lot near some hiking trails, and hiked through the wilderness.

If everyone bought an SUV and tore up the wilderness every weekend to "get away", there'd be nothing to get away to after a few years. Everyone would just be driving to SUV parks (kinda like trailer parks, except even trashier).

Thursday, December 6, 2007

gnome-volume-manager-2.22.0

Just released gnome-volume-manager-2.22.0 - a bit early for GNOME 2.22, but seeing as how I rarely ever hack on this project anymore, I figured I should actually release a new version (last release was 2.17.0 from presumably a year or more ago).

This new release adds integration with ConsoleKit for detecting whether the user is local and whether or not he/she is active at the console.

The other major change it includes is the ability to execute autorun.exe and autorun.inf files on cdroms so long as wine is installed on the system.

I implemented this feature toward the end of the summer because I had attempted to help my grandmother migrate from Windows95 to Linux. Unfortunately, while she was OK with the GNOME desktop itself for the most part, she wasn't comfortable using OpenOffice (she wanted Microsoft Office 97) and she wanted to use the proprietary versions of Mahjongg/Sudoku/etc games that she was used to running on her old Windows95 machine. As I began to show her how to install/run Windows software under Linux using wine, I realized that the process could be a whole lot simpler/intuitive than it was by making gnome-volume-manager auto-magically run autorun.exe and autorun.inf files under wine for her when she plopped the cd into the drive.

...and the rest is, as they say, history.

Saturday, November 10, 2007

Call of Duty 4

I just got my 40" HD widescreen LCD TV Friday morning and today I went out and bought Call of Duty 4 needing a new game to play now that I've beaten Rainbow Six: Vegas.

Wow. Just wow. This game is amazing.

Here's the trailer:

And here's some sample gameplay footage:

Update: Already beat the game... guess I'll go back now and try to get all the achievement points I missed.

Wednesday, November 7, 2007

Parsing Integers

Every once in a while, it becomes necessary to parse an integer from a string. The trick is doing so correctly and without the need to use an integer type larger than what you are parsing into (i.e. if you are trying to parse a 32bit integer, you don't want to have to use a temporary 64bit int value for checking overflow - this is especially desirable if you are trying to parse larger int values, such as a 64bit int because you won't likely have access to a 128bit int type).

The following macro correctly implements an integer parsing routine for a given number of bits:

#define STRTOINT(bits, max)                                     \
static int                                                      \
strtoint##bits (const char *in, char **inend,                   \
                int##bits##_t *retval, int *err)                \
{                                                               \
    register const char *inptr = in;                            \
    int##bits##_t val = 0;                                      \
    int digit, sign = 1;                                        \
                                                                \
    while (*inptr == ' ')                                       \
        inptr++;                                                \
                                                                \
    if (*inptr == '-') {                                        \
        sign = -1;                                              \
        inptr++;                                                \
    }                                                           \
                                                                \
    in = inptr;                                                 \
    while (*inptr >= '0' && *inptr <= '9')                      \
        digit = (*inptr - '0');                                 \
        if (val > (max / 10)) {                                 \
            *err = EOVERFLOW;                                   \
            return -1;                                          \
        } else if (val == (max / 10)) {                         \
            if (digit > (max % 10) &&                           \
                (sign > 0 || digit > ((max % 10) + 1))) {       \
                *err = EOVERFLOW;                               \
                return -1;                                      \
            }                                                   \
                                                                \
            if (sign < 0)                                       \
                val = (val * sign * 10) - digit;                \
            else                                                \
                val = (val * 10) + digit;                       \
                                                                \
            inptr++;                                            \
            if (*inptr >= '0' && *inptr <= '9') {               \
                *err = EOVERFLOW;                               \
                return -1;                                      \
            }                                                   \
                                                                \
            goto ret;                                           \
        } else {                                                \
            val = (val * 10) + digit;                           \
        }                                                       \
                                                                \
        inptr++;                                                \
    }                                                           \
                                                                \
    if (inptr == in) {                                          \
        *err = EINVAL;                                          \
        return -1;                                              \
    }                                                           \
                                                                \
    val *= sign;                                                \
                                                                \
 ret:                                                           \
    *inend = (char *) inptr;                                    \
    *retval = val;                                              \
                                                                \
    return 0;                                                   \
}

To expand this macro to parse a 32bit integer value from a string, you would do:

STRTOINT(32, 2147483647L)

The resulting function would be named strtoint32().

Why implement my own integer parsing routine instead of using libc's strtol(), you ask?

For a variety of reasons:

  • strtol() might not be available or you might want to implement an integer parsing routine in a language other than c/c++ (a variation of my routine is now used in Mono for all of the Int.Parse() methods).
  • strtol() is locale dependent which is not always a desirable trait.
  • strtol() requires annoying error checking that using my function makes trivial.
  • it's easy to extend my function macro to support integers of any bit size, e.g. 64bit integers (or someday 128bit integers).

To generate functions to parse 8, 16, 32, and 64bit integers, you would write:

STRTOINT(8, 127)
STRTOINT(16, 32767)
STRTOINT(32, 2147483647L)
STRTOINT(64, 9223372036854775807LL)

What about unsigned integers, you ask? Not to worry, I have an implementation for that as well:

#define STRTOUINT(bits, max)                                    \
static int                                                      \
strtouint##bits (const char *in, char **inend,                  \
                 uint##bits##_t *retval, int *err)              \
{                                                               \
    register const char *inptr = in;                            \
    uint##bits##_t val = 0;                                     \
    int digit;                                                  \
                                                                \
    while (*inptr == ' ')                                       \
        inptr++;                                                \
                                                                \
    in = inptr;                                                 \
    while (*inptr >= '0' && *inptr <= '9') {                    \
        digit = (*inptr - '0');                                 \
        if (val > (max / 10)) {                                 \
            *err = EOVERFLOW;                                   \
            return -1;                                          \
        } else if (val == (max / 10) && digit > (max % 10)) {   \
            *err = EOVERFLOW;                                   \
            return -1;                                          \
        } else {                                                \
            val = (val * 10) + digit;                           \
        }                                                       \
                                                                \
        inptr++;                                                \
    }                                                           \
                                                                \
    if (inptr == in) {                                          \
        *err = EINVAL;                                          \
        return -1;                                              \
    }                                                           \
                                                                \
    *inend = (char *) inptr;                                    \
    *retval = val;                                              \
                                                                \
    return 0;                                                   \
}

STRTOUINT(8, 255)
STRTOUINT(16, 65535)
STRTOUINT(32, 4294967295UL)
STRTOUINT(64, 18446744073709551615ULL)

And there you have it.

Note: these routines require [u]int[8,16,32,64]_t to be defined. If you are on Linux, #include <stdint.h> should do the trick.

Update: Miguel's blog goes into more detail on why this code was important for Mono.

Saturday, November 3, 2007

Oompa Loompa

Oompa. Loompa. Doompity dosum. Dwight is now gone which is totally awesome. -- Andy Bernard, The Office

Thursday, November 1, 2007

Re: Squandering one of the industry's best open source talents

Matt Asay, in a recent blog on CNET, tries to imply that Mono/Moonlight are a waste of Miguel's time/talents.

For these reasons I can't help but wonder why he's squandering his talents on writing largely irrelevant code (Mono, Moonlight) that appeals to himself, Novell, Microsoft, and no one else.

Really? No one else? Are you saying that the thousands of users and developers involved in the Mono community are nobodies? Are you saying that OTEE, the developers of Unity3D, are nobodies? Are you inferring that Codice Software and the growing list of companies/projects basing their software on Mono are nobodies?

Miguel says that he's doing this to bring the proprietary world into the open-source camp. It's not working, Miguel. You don't convince by capitulating. You convince by winning

Based on the growing number of users and developers getting involved with the Mono project (and recognizing that there are probably far more out there that just haven't made their presence known), I would say it is working. Because of the Mono project, more people are taking an interest in Open/Free Software, which I and many others would call "winning".

Rather, Miguel de Icaza can turn the industry on its head by putting his knowledge of interoperability and open source to work on developing the next-generation desktop (and not by recreating the "best" of Microsoft on Linux).

It's unfortunate that you are so short-sighted, Matt. That seems to be one of the more important differences between you and Miguel. Miguel sees the big picture. He knows that in order to create the "next-generation desktop", he'll need better tools in which to do it. He also sees that "winning" takes "developers, developers, developers!" He knows that you can't "win" if it takes developers on Linux 2 or 3 times as long to write an application as it takes to write on Windows with proprietary tools.

I think Miguel de Icaza is an exceptional developer. He's also a fantastically effective community leader.

All the more reason to let him continue doing what he does best.

Update: Joe Shaw makes an excellent point.

Update: It appears that Matt Asay has retracted his post and made an apology.

Code Snippet Licensing

All code posted to this blog is licensed under the MIT/X11 license unless otherwise stated in the post itself.