Monthly Archives: August 2008

Diversão com expressões regulares

Hoje meu amigo Metal estava com um probleminha pra escrever um mapeamento de URL para o Django. O Django usa uma idéia que eu acho meio estúpida, que é mapear expressões regulares para métodos, com partes capturadas sendo transformadas em argumentos. Flexível, mas muito propensa a erros e mais complexa do que eu acho que mapeamento de URLs tem que ser.

De qualquer forma, o método dele tinha de receber um endereço de email como único argumento. O mapeamento que ele estava usando era o seguinte:

  • (r’^login/(?P<e>.*)/?$’, ‘projeto.aplicacao.views.login’)

Isso fazia com que a variável ‘e’ do método login recebesse o endereço de email, quando o usuário acessasse a URL /login/endereco@deemail.com. O problema é que algumas vezes a URL acessada é /login/endereco@deemail.com/ (note a barra no final). “Ah, mas tem aquela barra depois do parentese, com o ponto de interrogação que serve justamente pra esse caso”, dirá você… e eu já te digo que se a barra estiver presente ela vai ser capturada para a variável ‘e’! Bug? Nah, só uma peculiaridade de como funcionam as expressões regulares.

Entender duas coisas simples faz você subir de nível em expressões regulares:

  1. ERs são batidas caractere a caractere
  2. * e + são gulosos, e vão comer tudo que você der pra eles

Um exemplo simples; considere o seguinte texto:

"a" b "c"

Se você aplicar a esse texto a seguinte expressão regular: “.*” o que você acha que vai bater? Vejamos? Note que as aspas duplas fazem parte da ER; as aspas simples são só pra impedir o shell de tentar expandir os caracteres especiais.

$ echo ‘”a” b “c”‘ | egrep –colour ‘”.*”‘
“a” b “c”

O egrep coloriu o texto inteiro; isso aconteceu porque a ER foi batida da seguinte forma:

  1. pega o primeiro caractere da ER, ; bate com o primeiro caractere da string, ? bate
  2. pega o segundo caractere da ER, ., ou seja, qualquer caractere; bate com o segundo caractere da string, a? bate
  3. pega o terceiro caractere da ER, *, opa, é só repetir o último o tanto que der agora…; bate com o terceiro caractere da string, ? bate
  4. ainda no terceiro caractere da ER, *; bate com o quarto caractere da string, [espaço_em_branco]? bate
  5. ainda no terceiro caractere da ER, *; bate com o oitavo caractere da string, c? bate
  6. ainda no terceiro caractere da ER, *; bate com o nono caractere da string, ? bate
  7. acabaram os caracteres da string… e agora? bom, pegamos o quarto caractere da ER, ; como estamos no final da string vamos voltar até achar um caractere que bata com ele… voltamos para o nono caractere da stirng, , bateu, acabou aqui

Esse último passo, voltar para tentar bater na string caracteres que ainda existem na ER é chamado de backtracking, e dependendo do tamanho do texto que está sendo processado pode destruir o desempenho da aplicação que estiver usando ER. Bom… mas como resolvemos isso?

$ echo ‘”a” b “c”‘ | egrep –colour ‘”[^"]*”‘
“a” b “c”

O que eu fiz? Basicamente eu troquei o . por uma expressão que diz não “. Ou seja, estou pedindo a ER para bater qualquer número de caracteres que não sejam aspas duplas. Vamos repetir o processo:

  1. pega o primeiro caractere da ER, “, bate com o primeiro caractere da string, “? bate
  2. pega o segundo caractere da ER, [^"], opa aqui temos uma expressão dizendo que não serve o caractere “; bate com o segundo caractere da string, a? bate
  3. pega o terceiro caractere da ER, *, opa, é só repetir o último o tanto que der agora…; bate com o terceiro caractere da string, “? não

O fato de termos usado uma expressão que diz, simplificando, não ” repetido n vezes fez com que o operador * parasse antes de chegar ao final do texto, o que nos poupou muito tempo, e ainda deu o resultado esperado. A solução para o problema do Metal era, portanto, trocar o . por [^/] na expressão dele. Dessa forma a barra, quando aparecia, era batida do lado de fora do parentese, e não era capturada.

Fica como exercício para o leitor imaginar o que acontece quando tentamos pegar o que existe, num gigantesco arquivo HTML, entre a tag <html> e a tag <head> usando a ER <html>.*<head>; eu já posso adiantar que não é nada eficiente… alguém se habilita a postar nos comentários? =D

Update: o coredump lembrou o operador *?, que é o * não-greedy; ele é útil, mas veja nos comentários por que ele não é uma solução para esse problema específico.

gksu-policykit progressing

So it is currently possible to use simple library calls in glib-based code to run something as root, by taking advantage of the gksu policykit mechanism:

GksuProcess* gksu_process_new(const gchar *working_directory, const gchar **arguments);
gboolean gksu_process_spawn_async(GksuProcess *process, GError **error);

The DBus service already works; it is able to setup the environment and X authorization correctly. There is still lots to do; startup notification is still not handled, and dealing with the application’s stdandard output and error messages, as well as providing a way for the caller to send stuff into the processe’s standard input. It is already possible to start an application and know that it has been finished, though.

As for the code:

$ git clone git://kov.eti.br/srv/git/gksu-polkit.git/

Criticism is welcome!

In other news… I’d like to ask our dear lazy web if anyone is using some nice way of providing only posts tagged in specific categories in a feed in wordpress. I’d like to use that to provide my posts to planet debian from my wordpress install.

Primeiro dia de debcamp!

Cheguei aqui… algumas coisas deram errado, outras certo, já deu pra me divertir um pouquinho e gastei um tempo preparando minha agenda pessoal. Eu fiz um arquivo ical com o que já planejei assistir, se alguém quiser acompanhar: http://people.debian.org/~kov/kov_debconf8.ical. Na porcaria bugada do Evolution você precisa criar um calendário ‘Na web’ e colocar webcal://people.debian.org/~kov/kov_debconf8.ical como endereço.

Agora é me preparar pra hackear bastante o gksu policykit durante os próximos dias! See you at debconf!