Some posts of this blog are indexed by Planet Debian and Planet Haskell.

RSS Atom Add a new post titled:

[...] it is absurd to make elaborate security checks on debugging runs, when no trust is put in the results, and then remove them in production runs, when an erroneous result could be expensive or disastrous. What would we think of a sailing enthusiast who wears his life-jacket when training on dry land but takes it off as soon as he goes to the see?

Hoare, C. A. R. Hints on programming-language design. In Hoare and Jones. Essays in Computing Science. Prentice-Hall. 1989. pg. 198.

as mentioned on

Paulson, L. C. ML for the Working Programmer. Cambridge. 2nd edition. pg. 15.

Posted Qua 14 Nov 2012 22:33:33 UTC

É só instalar o pacote texlive-lang-portuguese. \usepackage[T1]{fontenc} não muda nada.

Posted Qua 31 Out 2012 21:16:00 UTC

I was trying to build the curl hackage package on Debian unstable with ghc 7.0.3, and one of its modules were failing to build. I searched for the error message and found this GHC ticket. As mentioned in the ticket, I had to downgrade binutils to 2.20. The version of binutils in sid is 2.21.

Another possibility would be to change curl to avoid using -fvia-C, but I didn't want to modify the package.

Posted Seg 30 Jul 2012 12:45:45 UTC Tags: ?debian ?haskell

A Marina Silva deu a entender que não existe voto útil no primeiro turno. Gostaria de discordar. Suponhamos um cenário, similar ao mostrado no vídeo, em que Pepperoni tem 40% dos votos válidos, Mussarela 25%, Marguerita 20% e os outros sabores misturados 15%. Suponhamos ainda, que meu candidato preferido é um dos sabores misturados, e meu segundo candidato preferido é Marguerita. Mussarela é o pior candidato na minha opção. Dessa forma, eu prefiro que haja um segundo turno entre Pepperoni e Marguerita do que entre Pepperoni e Mussarela. Mas, se eu votar no meu candidato preferido, estarei tirando um voto da Marguerita, diminuindo suas chances no segundo turno, passando o voto para um candidato que tem menos chance de chegar no segundo turno. Por outro lado, posso fazer um voto útil, votando na Marguerita, escolhendo assim, o menos pior, já no primeiro turno.

Troquem Pepperoni por Dilma, Mussarela por Serra, Marguerita por Marina, e outro sabores pelos outros candidatos, ajustem as porcentagens e terão uma idéia da minha intenção de voto. Eu votaria no Plínio, mas votarei na Marina como voto útil.

A raiz desse problema é que o segundo turno só tem 2 candidatos. Se a eleição fosse em N turnos, esse problema estaria resolvido. Ou, se fosse usado, como no Debian, o Método de Condorcet, esse problema também não existiria.

Não faz sentido deixar de votar no "terceiro lugar" para votar no "segundo lugar" como voto útil, mas faz sentido deixar de votar no "quarto lugar" para votar no "terceiro lugar" e aumentar suas chances de disputar o segundo turno.

Espero que essa análise (esteja correta e) te ajude a votar.

Posted Seg 30 Jul 2012 12:45:45 UTC
The x field in the permissions on a directory does not mean execution; it means "search". Execute permission on a directory determines whether the directory may be searched for a file. It is therefore possible to create a directory with mode --x for other users, implying that users may access any file that they know about in that directory, but may not run ls on it or read it to se what files are there. Similarly, with directory permissions r--, users can see (ls) but not use the contents of a directory. Some installations use this device to turn off /usr/games during busy hours.

Brian W. Kernighan, Rob Pike. The UNIX Programming Environment. Prentice-Haskell Software Series, 1984, section 2.4, page 56.

Posted Seg 30 Jul 2012 12:45:45 UTC

git doesn't have a feature as simple and useful as darcs setpref test. To emulate it, I have created a script in .git/hooks/pre-commit. For instance, if the use was darcs setpref test make, the git script goes as:

erase() {
    rm -rf /tmp/workdir$$ /tmp/patch$$
}

handle() {
    erase
    exit 1
}

git new-workdir `git rev-parse --show-toplevel` /tmp/workdir$$
git diff --cached > /tmp/patch$$
cd /tmp/workdir$$
patch -p1 < ../patch$$
make || handle
erase

By the way, I miss darcs revert, and specially darcs unrevert. It's possible to emulate darcs revert -a using git reset --hard, but this comes without darcs unrevert latter. Yes, there is git stash, but it's not the same thing. Also, I could not find a way to have something like darcs revert.

Posted Seg 30 Jul 2012 12:45:45 UTC Tags: ?debian

This is the first post to this example blog. To add new posts, just add files to the posts/ subdirectory, or use the web form.

Posted Seg 30 Jul 2012 12:45:45 UTC

What's the difference between

$ mv junk junk1

and

$ cp junk junk1
$ rm junk

Hint: make a link to junk, then try it.

Brian W. Kernighan, Rob Pike. *The UNIX Programming Environment*. Prentice-Haskell Software Series, 1984, exercise 2-7.

Posted Seg 30 Jul 2012 12:45:44 UTC
>>> x = ['a', 'b']
>>> x
['a', 'b']
>>> x += 'c'
>>> x
['a', 'b', 'c']
>>> x += ''
>>> x
['a', 'b', 'c']
>>> x += ['']
>>> x
['a', 'b', 'c', '']
Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian ?haskell

For some GTK+ themes, icons are not shown in buttons and in menus. They can be made visible again editing some gconf keys, possibly with gconf-editor. The keys to be changed are /desktop/gnome/interface/buttons_have_icons for buttons and /desktop/gnome/interface/menus_have_icons for menus.

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

This is mostly inspired on this article. The main difference is that I talk about sup instead of mutt, and use SpamAssassin together with bogofilter. Having all these utilities installed and the sources for sup configured, you only need to touch procmail and sup configuration files.

My ~/.procmailrc contains a lot of comments, but what it does is basicly the following: If the message is smaller than 250 KiB, it calls SpamAssassin with it. This will create the headers X-Spam-Level and X-Spam-Status in this message. Only messages smaller than 250KiB are considered because most spam isn't bigger than that, and considering big messages would overload SpamAssassin. Then, if SpamAssassin consider this message as spam, tell bogofilter about it. Next, call bogofilter, which will add the X-Bogosity header to the message.

Now we have to configure sup. We need to tell it to update bogofilter when we manually mark a message as spam. For this we can use ~/.sup/hooks/mark-as-spam.rb. We must consider two cases: the case where bogofilter thought it was not spam, and the case where bogofilter was not sure about its status. In the first case, we must tell it to forget that the message was not spam and to register it as spam now. In the second, just register it as spam.

Also, we don't want spam on inbox. So we can edit ~/.sup/hooks/before-add-message.rb to look for the headers generated by SpamAssassin and Bogofilter and remove the :inbox label. It's important to mark unsure messages with a label so that we don't forget to mark it as spam if it is, and as not spam if it's not.

So we need a way to mark messages as not spam. I included a new keybinding in ~/.sup/hooks/startup.rb. Unfortunatelly, I don't know how to include a new keybinding in sup without repeating all the keybindings for the mode I'm dealing with, so that's how I've made it. The command removes the :unsure label and mark it as non-spam. It also sets the undo command, unregistering the message in bogofilter. The case where the message was previously marked as spam, and not unsure, is also handled.

I'm sure there's a lot of room for improvement, specially in the Ruby part, but it seems to be working.

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

I'm now using ikiwiki with darcs in my own server and saying goodbye to wordpress.com. I tried to install wordpress before trying ikiwiki, but it was just eating all the memory of the server. And after using ikiwiki, I thanked wordpress for misbehaving, because ikiwiki is so much better. It's everything I wanted, but I didn't knew I wanted until I tried it. Just like sup.

My homepage, which used to be my page on the Debian wiki is not being hosted here too, and it's great to have it so nicely integrated with the blog.

I also thought about trying gitit, since it's written in Haskell, but it doesn't seem to support blog easily. And now I'm so happy with ikiwiki that I doubt I'll change.

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

[...] read also says how many bytes of the file were returned, so end of file is assumed when a read says "zero bytes are being returned." [...]

When a program reads from your terminal, each input line is given to the program by the kernel only when you type its newline (i.e, press RETURN). [...]

[...]

Now try something different: type some characters and then a *ctl*-d rather than a RETURN:

$ cat -u
123
ctl-d123

cat prints the characters out immediately. *ctl*-d says, "immediately send the characters I have typed to the program that is reading from my terminal." The *ctl*-d itself is not sent to the program, unlike a newline. Now type a second ctl-d, with no other chracters:

$ cat -u
123
ctl-d123ctl-d$

The shell responds with a prompt, because cat read no characters, decided that meant end of file, and stopped. ctl-d sends whatever you have typed to the program that is reading from the terminal. If you haven't typed anything, the program will therefore read no characters, and that looks like the end of the file. That is why *ctl*-d logs you out --- the shell sees no more input. Of course, *ctl*-d is usually used to signal an end-of-file but it is interesting that is has a more general function.

Brian W. Kernighan, Rob Pike. The UNIX Programming Environment. Prentice-Haskell Software Series, 1984, section 2.1, pages 44-45.

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

My problem was: I had a number of databases generated in different machines and I wanted to query them as if they were one, using the database the data came from as a field while querying and while showing results. The databases are SQLite3 files, generated using SQLAlchemy in a Python program.

I solved this by using SQLAlchemy, which was good because I could use the same ORM mapping that the program used. I noticed that the Horizontal Sharding SQLAlchemy extension would fit well the problem, although not perfectly. I had to make some changes in some classes of this extension, and now it works fine.

It was possible to filter the data using database as a criteria, but I couldn't get the database information from each line of a query result. I made a simple patch to SQLAlchemy, which wasn't likely to be introduced in the distribution, but worked for me, and sent it to its bug tracker. The change was included in SQLAlchemy in a very different fashion, as expected, but since I'm using the released version from SQLAlchemy, I kept on using my version of the patch. I don't want to do direct changes in SQLAlchemy source code, so I made the change in my program:

class ShardedSessionShardId(ShardedSession):
    def __init__(self, *args, **kwargs):
        super(ShardedSessionShardId, self).__init__(*args, **kwargs)
        self._query_cls = ShardedQueryShardId

class ShardedQueryShardId(ShardedQuery):
    def _execute_and_instances(self, context):
        if self._shard_id is not None:
            result = self.session.connection(
                            mapper=self._mapper_zero(),
                            shard_id=self._shard_id).execute(context.statement, self._params)

            news = list(self.instances(result, context))
            for new in news:
                new.shard_id = self._shard_id
            return iter(news)

        else:
            partial = []
            for shard_id in self.query_chooser(self):
                result = self.session.connection(
                            mapper=self._mapper_zero(),
                            shard_id=shard_id).execute(context.statement, self._params)

                news = list(self.instances(result, context))
                for new in news:
                    new.shard_id = shard_id
                partial = partial + news

            # if some kind of in memory 'sorting'
            # were done, this is where it would happen
            return iter(partial)

create_session = sessionmaker(class_=ShardedSessionShardId)

Another problem is that I had to make each result be included in the query, even if two results from different DBs have the same primary key. I achieved this by changing two classes: WeakInstanceDict, and Mapper. For using the new WeakInstanceDict, I had again to change the ShardedSession variation:

class WeakInstanceDictNoIdentity(WeakInstanceDict):
    def add(self, state):
        # if state.key in self:
        #     if dict.__getitem__(self, state.key) is not state:
        #         raise AssertionError("A conflicting state is already "
        #                             "present in the identity map for key %r"
        #                             % (state.key, ))
        # else:
            dict.__setitem__(self, state.key, state)
            self._manage_incoming_state(state)

class ShardedSessionShardId(ShardedSession):
    def __init__(self, *args, **kwargs):
        super(ShardedSessionShardId, self).__init__(*args, **kwargs)
        self._query_cls = ShardedQueryShardId
        self._identity_cls = WeakInstanceDictNoIdentity
        self.identity_map = self._identity_cls()

To start using the new Mapper, I simply replaced each call to mapper with MapperNoIdentity:

class MapperNoIdentity(Mapper):
    def _instance_processor(self, context, path, adapter,
                                polymorphic_from=None, extension=None,
                                only_load_props=None, refresh_state=None,
                                polymorphic_discriminator=None):

        """Produce a mapper level row processor callable
           which processes rows into mapped instances."""

        pk_cols = self.primary_key

        if polymorphic_from or refresh_state:
            polymorphic_on = None
        else:
            if polymorphic_discriminator is not None:
                polymorphic_on = polymorphic_discriminator
            else:
                polymorphic_on = self.polymorphic_on
            polymorphic_instances = util.PopulateDict(
                                        self._configure_subclass_mapper(
                                                context, path, adapter)
                                        )

        version_id_col = self.version_id_col

        if adapter:
            pk_cols = [adapter.columns[c] for c in pk_cols]
            if polymorphic_on is not None:
                polymorphic_on = adapter.columns[polymorphic_on]
            if version_id_col is not None:
                version_id_col = adapter.columns[version_id_col]

        identity_class = self._identity_class
        def identity_key(row):
            return identity_class, tuple([row[column] for column in pk_cols])

        new_populators = []
        existing_populators = []
        load_path = context.query._current_path + path

        def populate_state(state, dict_, row, isnew, only_load_props):
            if isnew:
                if context.propagate_options:
                    state.load_options = context.propagate_options
                if state.load_options:
                    state.load_path = load_path

            if not new_populators:
                new_populators[:], existing_populators[:] = \
                                    self._populators(context, path, row,
                                                        adapter)

            if isnew:
                populators = new_populators
            else:
                populators = existing_populators

            if only_load_props:
                populators = [p for p in populators
                                if p[0] in only_load_props]

            for key, populator in populators:
                populator(state, dict_, row)

        session_identity_map = context.session.identity_map

        if not extension:
            extension = self.extension

        translate_row = extension.get('translate_row', None)
        create_instance = extension.get('create_instance', None)
        populate_instance = extension.get('populate_instance', None)
        append_result = extension.get('append_result', None)
        populate_existing = context.populate_existing or self.always_refresh
        if self.allow_partial_pks:
            is_not_primary_key = _none_set.issuperset
        else:
            is_not_primary_key = _none_set.issubset

        def _instance(row, result):
            if translate_row:
                ret = translate_row(self, context, row)
                if ret is not EXT_CONTINUE:
                    row = ret

            if polymorphic_on is not None:
                discriminator = row[polymorphic_on]
                if discriminator is not None:
                    _instance = polymorphic_instances[discriminator]
                    if _instance:
                        return _instance(row, result)

            # determine identity key
            if refresh_state:
                identitykey = refresh_state.key
                if identitykey is None:
                    # super-rare condition; a refresh is being called
                    # on a non-instance-key instance; this is meant to only
                    # occur within a flush()
                    identitykey = self._identity_key_from_state(refresh_state)
            else:
                identitykey = identity_key(row)

            # instance = session_identity_map.get(identitykey)
            # if instance is not None:
            #     state = attributes.instance_state(instance)
            #     dict_ = attributes.instance_dict(instance)

            #     isnew = state.runid != context.runid
            #     currentload = not isnew
            #     loaded_instance = False

            #     if not currentload and \
            #             version_id_col is not None and \
            #             context.version_check and \
            #             self._get_state_attr_by_column(
            #                     state,
            #                     dict_,
            #                     self.version_id_col) != \
            #                             row[version_id_col]:

            #         raise orm_exc.ConcurrentModificationError(
            #                 "Instance '%s' version of %s does not match %s"
            #                 % (state_str(state),
            #                     self._get_state_attr_by_column(
            #                                 state, dict_,
            #                                 self.version_id_col),
            #                         row[version_id_col]))
            # elif refresh_state:
            if refresh_state:
                # out of band refresh_state detected (i.e. its not in the
                # session.identity_map) honor it anyway.  this can happen
                # if a _get() occurs within save_obj(), such as
                # when eager_defaults is True.
                state = refresh_state
                instance = state.obj()
                dict_ = attributes.instance_dict(instance)
                isnew = state.runid != context.runid
                currentload = True
                loaded_instance = False
            else:
                # check for non-NULL values in the primary key columns,
                # else no entity is returned for the row
                if is_not_primary_key(identitykey[1]):
                    return None

                isnew = True
                currentload = True
                loaded_instance = True

                if create_instance:
                    instance = create_instance(self,
                                                context,
                                                row, self.class_)
                    if instance is EXT_CONTINUE:
                        instance = self.class_manager.new_instance()
                    else:
                        manager = attributes.manager_of_class(
                                                instance.__class__)
                        # TODO: if manager is None, raise a friendly error
                        # about returning instances of unmapped types
                        manager.setup_instance(instance)
                else:
                    instance = self.class_manager.new_instance()

                dict_ = attributes.instance_dict(instance)
                state = attributes.instance_state(instance)
                state.key = identitykey

                # manually adding instance to session.  for a complete add,
                # session._finalize_loaded() must be called.
                state.session_id = context.session.hash_key
                session_identity_map.add(state)

            if currentload or populate_existing:
                if isnew:
                    state.runid = context.runid
                    context.progress[state] = dict_

                if not populate_instance or \
                        populate_instance(self, context, row, instance,
                            only_load_props=only_load_props,
                            instancekey=identitykey, isnew=isnew) is \
                            EXT_CONTINUE:
                    populate_state(state, dict_, row, isnew, only_load_props)

            else:
                # populate attributes on non-loading instances which have
                # been expired
                # TODO: apply eager loads to un-lazy loaded collections ?
                if state in context.partials or state.unloaded:

                    if state in context.partials:
                        isnew = False
                        (d_, attrs) = context.partials[state]
                    else:
                        isnew = True
                        attrs = state.unloaded
                        # allow query.instances to commit the subset of attrs
                        context.partials[state] = (dict_, attrs)

                    if not populate_instance or \
                            populate_instance(self, context, row, instance,
                                only_load_props=attrs,
                                instancekey=identitykey, isnew=isnew) is \
                                EXT_CONTINUE:
                        populate_state(state, dict_, row, isnew, attrs)

            if loaded_instance:
                state._run_on_load(instance)

            if result is not None and \
                        (not append_result or
                            append_result(self, context, row, instance,
                                    result, instancekey=identitykey,
                                    isnew=isnew)
                                    is EXT_CONTINUE):
                result.append(instance)

            return instance
        return _instance

I had to include some auxiliary definitions to make the rewrites work:

_none_set = frozenset([None])
_runid = 1L
_id_lock = util.threading.Lock()
def _new_runid():
    global _runid
    _id_lock.acquire()
    try:
        _runid += 1
        return _runid
    finally:
        _id_lock.release()

It would be good to be able to set these identity requirements as a parameter.

My last problem was selecting more than one database to search. setshard only worked for one, so I created a new field in query, called shards, and checked for it on querychooser:

def query_chooser(query):
    try:
        return query.shards
    except AttributeError:
        pass
    return tcs.keys()

So, when I want to look only in a list of shards, I set this field. I'm aware that this is not a recommended python idiom, but, well, it works fine.

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

After testing Emacs, Eclipse, KDevelop and CodeBlocks for writing C++ code, I decided to stick with Netbeans. It seems to be the most simple to configure and yet full of features and plugins. I was having one problem with it that when I focused out the window, and then focused on it again, it would not really grab the focus, in the sense that I would not be able to type without first clicking with the mouse. At first I thought the problem was with Netbeans, then with the JRE. I tried using sun-java6-jre, I tried upgrading my openjdk-6-jre, and nothing worked. I searched a little bit more and got to a discussion about this problem in ion3, and that made me think that the problem could be related to the window manager I use, XMonad. After searching a bit about it, I found on the XMonad FAQ some work arounds for problems with Java apps, but they didn't solve my problem. Then, I found the solution on this bug report. I got the darcs version of xmonad and XMonadContrib, included takeTopFocus on logHook, and now it's working!

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian ?haskell

In Portuguese we use the word vaquinha, which means little cow, to refer to a group of people contributing money for some common goal. In a meeting of the organization of the DebConf12 BID of Belo Horizonte, I, Rafael, Régis and Samuel decided to create a vaquinha to become a sponsor of DebConf11. With the help of Amazing Valéssio, we created a website and we are collecting donations. The idea is simple: people donate and their name is shown on the website. If they give more than R$50,00, they also receive an exclusive T-shirt. We mixed the idea of vaquinha with the Super Cow Powers from APT. The site is only in Portuguese, since our main focus is to ask for donations from brazilians, but nothing stops foreigns from donating. Our plan is to become Bronze Sponsors, but if we can't all that money, we'll just give to DebConf11 whatever we have. I hope you like the idea, and maybe have a similar initative in your country.

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

I just discovered sshpass and saw some comments of people complaining that there's no good use case that justifies it. Well, I have a use case. That's in fact what made me search about the theme and found it. I created a user on a machine and I can't remember which password I used, but I know it's probably one of a small list I can think of. The list is composed of variations of the same string, in a way that I could generate with a program. It would take me time to try all the passwords by hand, so using sshpass make things much easier:

$ generate-password > pass-list
$ for i in `cat pass-list`; do sshpass -p $i ssh host; done
Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

I'm using GTK+ in my paid work, which is being cool because I like this library. It's not the first time I use it for paid work, and I like these opportunities to learn more about it and possibly report bugs. It's not easy to find bugs in such a widely used library, so much of them end up being simple cosmetic mistakes, like this one. Some others are only a question of interpretation, like this other, which even when I submitted a patch, it was not applied.

Yesterday I couldn't make popup-menu create a, well, popup menu. It was working with button-press-event, but not with popup-menu. I created a small code sample to illustrate this problem and asked about it in #pygtk@irc.gimp.org --- yes, I'm using Python:

import gtk
import gobject

def menu():
  item = gtk.ImageMenuItem('gtk-add')
  item.show()

  menu = gtk.Menu()
  menu.add(item)

  return menu

def on_view_popup_menu(obj, *data):
  print 'popup-menu'
  menu().popup(None, None, None, 0, 0)

def on_view_button_press_event(obj, *data):
  print 'button-press-event'
  menu().popup(None, None, None, 0, 0)

store = gtk.ListStore(gobject.TYPE_STRING)
store.append(('teste',))

view = gtk.TreeView(store)
view.append_column(gtk.TreeViewColumn('Coluna', gtk.CellRendererText(), text=0))
view.connect('button-press-event', on_view_button_press_event)
view.connect('popup-menu', on_view_popup_menu)
view.show()

window = gtk.Window()
window.add(view)
window.show()

gtk.main()

When I clicked in the window, with any button in this case, 'button-press-event' is printed to stdout and the menu is displayed. When I press the Menu key, or Shift+F10, 'popup-menu' is printed to stdout, but the menu is not displayed. After getting no response in #pygtk, I decided to try #gtk+. Before that, I thought about trying the same problem in C to see if it was not a problem in the bindings, or in how I was using it. The code:

#include <gtk/gtk.h>

GtkMenu *menu() {
  GtkImageMenuItem *image_menu_item =
      GTK_IMAGE_MENU_ITEM(
          gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, NULL));
  gtk_widget_show(GTK_WIDGET(image_menu_item));

  GtkMenu *menu = GTK_MENU(gtk_menu_new());
  gtk_container_add(GTK_CONTAINER(menu), GTK_WIDGET(image_menu_item));

  return menu;
}

void on_tree_view_popup_menu(GtkWidget *widget, gpointer user_data) {
  printf("popup-menu\n");
  gtk_menu_popup(
      menu(), NULL, NULL, NULL, NULL, 0, gdk_event_get_time(NULL));
}

void
on_tree_view_button_press_event(
  GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
  printf("button-press-event\n");
  gtk_menu_popup(
      menu(), NULL, NULL, NULL, NULL, 0, gdk_event_get_time(NULL));
}

int main(int argc, char **argv) {
  gtk_init(&argc, &argv);

  GtkListStore *list_store = gtk_list_store_new(1, G_TYPE_STRING);
  GtkTreeIter iter;
  gtk_list_store_append(list_store, &iter);
  gtk_list_store_set(list_store, &iter, 0, "teste", -1);

  GtkTreeView *tree_view =
      GTK_TREE_VIEW(
          gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store)));
  gtk_tree_view_append_column(
      tree_view,
      gtk_tree_view_column_new_with_attributes(
          "Coluna", gtk_cell_renderer_text_new(), "text", 0, NULL));
  g_signal_connect(
      tree_view, "popup-menu", G_CALLBACK(on_tree_view_popup_menu), NULL);
  g_signal_connect(
      tree_view,
      "button-press-event",
      G_CALLBACK(on_tree_view_button_press_event),
      NULL);
  gtk_widget_show(GTK_WIDGET(tree_view));

  GtkWindow *window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(tree_view));
  gtk_widget_show(GTK_WIDGET(window));

  gtk_main();
}

Wow. The difference between the codes is impressive. I'm not that used to code in GTK+ using C, and I may have missed something here, but the C code is so verbose that I get tired only by looking at it. I'm glad we have other languages, and GTK+ bindings for them. As expected, the behavior was the same, and I asked in #gtk+ and got no answer.

Yes, I did read the documentation before asking, and no, I didn't read it carefully enough. Maybe because of coming from Python, I didn't checked very much the type of the callback function for the signals, and didn't notice the point about the return value: "Returns: TRUE if a menu was activated". And that was not where I was looking for the problem, since I thought the return value was usually related to the treatment of the signal by another functions. Anyway, I changed the type of on_tree_view_popup_menu to return gboolean and returned TRUE. It works! I also updated the type of on_tree_view_button_press_event to do the same, just for compliance. In Python, a simple return True would do it.

I know I could use the event parameter in on_tree_view_button_press_event, but the point was exactly to run the same code and see if it would work in one case and not in the other, as happened.

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

There was a feature missing in darcs-monitor which I wanted: to send the e-mail to multiple recipients when a new patch arrives. I tried using:

apply posthook darcs-monitor --charset=UTF-8 email addr1@example.com,addr2@example.com

But it didn't worked. I also tried "addr1@example.com, addr2@example.com", but it also didn't worked. So I included this feature in darcs-monitor and released a new version. I also updated my e-mail address and darcs repository address.

While I was working at the debian package, I noticed a bug in 0.3.8-1, and made a upload of 0.3.8-2 to fix it. I also uploaded the new 0.4.0 to experimental.

I'm another one using flattr now. I've created my blog as a thing:

Flattr this

I also created a thing for darcs-monitor:

Flattr this

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian ?haskell

grep supports tagged regular expression, which is not supported by egrep. I could not find documentation about this feature in grep's manual, so I read about it on ed's manual. I found out about the feature reading The UNIX Programming Environment. It enables you to create sub-expressions inside a regular expression, and to make references to it while matching. For instance, you can search for two repeated characters with ' (.)\1 ', and for 5 letter palindromes with ' ([a-zA-Z])([a-zA-Z])[a-zA-Z]\2\1 '.

Posted Seg 30 Jul 2012 12:45:44 UTC

I've been playing with C++ lately, and it's an interesting language. Some random thoughts go here. First, in which situation does "... int x; x = 0 ..." is different from "... int x = 0 ... "? The answer on the end of the post.

Second, consider this code:

class A {};
// void f(const A &a) {}
// void f(A a) {}
void f(A &a) {}
int main() {
  f(A());
  // A a;
  // f(a);
}

It doesn't build under g++ or clang. Some say it builds under MSVC. If the definition of f is swapped by any of the commented ones, it works. If the declaration and call to f on function main is swapped by the commented lines, it also works.

Third, dynamic dispatch only works with pointers. Consider this code:

class A {
 public:
  virtual void f() {
    printf("A");
  }
};
class B : public A {
  void f() {
    printf("B");
  }
};
int main() {
  A a = B();
  a.f();
  A *p = new B();
  p->f();
  delete p;
  B b;
  A *q = &b;
  q->f();
}

It prints ABB. Some things to remember.

The answer of the first point is: those codes are different if there is a static before them. This can only happen when they are in the body of a function. In this case, the first would assign x to 0 at each call to the function, and the other only at the first call.

Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

On Bash, while "$*" expands to "$1c$2c...", where c is the IFS separator, "$@" expands to "$1" "$2" ..., which is very useful for scripts that take file names with space as a parameter. I got to this problem while writing an script to replace rm with cp -r followed by rm.

Posted Seg 30 Jul 2012 12:45:44 UTC

I needed a way to generate a list of commands in make automatically. I was using $(foreach i,$(list),cmd $i;), that is, a semicollon to intercalate commands. The problem with this approach is that if the first command fails, make would not detect the error. After searching for a while, I found the solution on this link. It's possible to create a newline in make functions using this technique. The code ended up being:

define \n


endef
target:
        $(foreach i,$(list),cmd $i$(\n))
Posted Seg 30 Jul 2012 12:45:44 UTC Tags: ?debian

From bash(1):

for name [ [ in [ word ... ] ] ; ] do list ; done

[...]

If the in word is omitted, the for command executes list once for each positional parameter that is set

Posted Seg 30 Jul 2012 12:45:44 UTC

Resposta à carta aberta aos machos.

Não concordo. Não sou babaca. A culpa das mulheres apanharem não é minha. Eu não bato em minha mulher. Sou homem, e acho isso muito legal. Sou hetero, e acho isso tão legal quanto ser homossexual.

O machismo não aflige só as mulheres, mas aos homens também. É claro que aflige de maneira diferente, mas não acho tão óbvio que seja uma aflição de amplitude tão diferente. Os papéis de gênero são impostos para homens e mulheres, todos nós sofremos com isso, e todos nós temos o direito de celebrar nosso gênero, com todos os seus problemas.

Existem homens machistas, existem mulheres machistas, existem homens feministas, existem mulheres feministas, existem negros racistas, existem brancos racistas. Eu já sofri preconceito por ser homem, já sofri preconceito por ser heterossexual e por ser branco. E em todas as vezes que isso aconteceu, veio justamente das pessoas que se consideram mais esclarecidas ou que dizem lutar contra o preconceito.

Essa visão de mulheres como vítimas e homens como carrascos, ao meu ver, decorre da necessidade maniqueísta de colocar o bem de um lado e o mal de outro, preto e branco.

Viva o dia dos homens! Viva o dia das mulheres! Vamos celebrar a diversidade, e acabar com essa história de um lado ser melhor do que o outro.

Posted Seg 30 Jul 2012 12:45:44 UTC

This blog is powered by ikiwiki.