Quinta-feira, 21 de Maio de 2009

Do fundo do baú





Em algum outro baú, mais fundo, devo ter as apostilas dos cursos. :-)

Quinta-feira, 7 de Maio de 2009

Disparador de programas para Sawfish

Havia tempos que eu queria um disparador de aplicações para usar com o Sawfish. Nunca tinha encontrado um que tivesse me agradado. Basicamente, gostaria de um que fosse simples, permitisse completar o nome de programas com TAB, que fosse rápido e que não ocupasse muita memória. Com uma rápida pesquisa na Internet, achei o código em http://www.skamphausen.de/cgi-bin/ska/download/runner.jl, o qual faz exatamente o que eu queria e satisfaz todos os meus requisitos: como é implementado em rep, não é preciso nem criar um novo processo; e a implementação é minúscula.

Depois de uma leve refatorada no código, estou usando o seguinte:

(require 'prompt)

(define (uniq l #!optional in)
;; Elimina replicas na lista L usando o procedimento IN para
;; verificar se um dado elemento se encontra na lista.
(let ((unique '()))
(let loop ((l l))
(if (null l)
unique
(let ((head (car l)))
(unless ((or in member) head unique)
(setq unique (cons head unique)))
(loop (cdr l)))))
(reverse unique)))

(define runner
(let* ((paths (uniq (delete-if-not file-directory-p
(string-split ":" (getenv "PATH")))))
(completions (uniq (apply append (mapcar directory-files paths)))))
(lambda ()
(interactive)
(let ((cmd (prompt-from-list completions "Run: " "" t)))
(system (concat cmd " &"))))))


Adicionei o procedimento uniq para eliminar eventuais réplicas na lista de comandos. Ela é mais flexível que uniquify-list, do Sawfish (a comparação é feita exclusivamente usando eq). uniq aceita um argumento opcional que é o procedimento de comparação (em caso de omissão, usa member).

O procedimento runner monta uma lista de elementos para serem usados como possíveis alternativas para completar palavras digitadas pelo usuário (tab completion), a qual é usada por prompt-from-list, do módulo prompt. As alternativas são os arquivos nos diretórios apontados pela variável de ambiente PATH.

Associei o procedimento runner à combinação de teclas H-RET (Hyper-Enter):

(bind-keys global-keymap
...
"H-RET" (lambda () (runner))
...)


A seguir está um screenshot:



O prompt criado pelo procedimento prompt-from-list define um mapa de teclas. Assim, quando em uso, o prompt aceita as seguintes combinações de teclas associadas a procedimentos (de prompt.jl, so Sawfish):

(bind-keys prompt-keymap
"ESC" prompt-exit
"C-g" prompt-exit
"C-u" prompt-clear
"BS" prompt-backspace
"C-k" prompt-kill-line
"Left" prompt-backward-character
"C-b" prompt-backward-character
"Right" prompt-forward-character
"C-f" prompt-forward-character
"C-Left" prompt-backward-word
"M-b" prompt-backward-word
"A-b" prompt-backward-word
"C-Right" prompt-forward-word
"M-f" prompt-forward-word
"A-f" prompt-forward-word
"C-a" prompt-beginning-of-line
"C-e" prompt-end-of-line
"TAB" prompt-complete
"RET" prompt-accept
"Up" prompt-previous
"Down" prompt-next
"M-n" prompt-next
"M-p" prompt-previous
"A-n" prompt-next
"A-p" prompt-previous))

Domingo, 26 de Abril de 2009

Sawfish

O Sawfish é um dos softwares da lista dos que eu não gostaria de parar de usar. Assim como o Emacs, é o tipo de software que, depois que se entende e aprende a usar, é difícil de largar.

O Sawfish durante algum tempo foi o gerenciador de janelas do ambiente de desktop Gnome. Por razões ainda não muito claras para mim, foi substituído pelo Metacity.

Assim como o Emacs, um dos grandes diferenciais do Sawfish é o fato de ser programável em uma linguagem de alto nível da família Lisp. Sawfish usa a linguagem rep (Read, Eval, Print), a qual inicialmente foi inspirada por Elisp (Emacs Lisp). A concepção do Sawfish segue a mesma linha do Emacs: prover uma API em uma linguagem de alto nível para desenvolvimento voltado às aplicações a que se destina (abstração). Manipulação de texto no caso do Emacs e gerenciamento de janelas no caso do Sawfish. Com base na API de alto nível, os aplicativos são desenvolvidos. Assim são desenvolvidos o Emacs e o Sawfish. A maior parte do código do Emacs é em Elisp e a maior parte do código do Sawfish é em rep.

O fato de prover uma API para desenvolvimento em uma linguagem de alto nível faz com que o Sawfish seja altamente (e facilmente!) personalizável e extensível. Além das possibilidades de configuração e extensão através de código em rep, Sawfish também dispõe de um configurador gráfico, o qual, por baixo dos panos, apenas gera e executa código rep para configurar o gerenciador de janelas (similar à interface para personalização do Emacs).



Seguindo a tradição de linguagens Lisp e de aplicativos que usam Lisp como linguagem de extensão/implementacao, Sawfish também oferece um REPL para avaliação de expressões rep. Abaixo está um exemplo de sessão com o REPL do Sawfish:

$ sawfish-client
sawfish 1.3.3, Copyright (C) 1999-2000 John Harper
sawfish comes with ABSOLUTELY NO WARRANTY; for details see the file COPYING

Enter `,help' to list commands.
user> (define emacs (get-window-by-name-re "emacs.*"))
user> emacs
#<window 1e00011>
user> (window-name emacs)
"emacs@mandolate"
user> (window-visible-p emacs)
t
user> (window-iconified-p emacs)
()
user> (window-dimensions emacs)
(798 . 742)
user> (window-border-width emacs)
0
user> (window-framed-p emacs)
t
user> (window-depth emacs)
0
user> (window-absolute-position emacs)
(68 . 0)
user> (window-sticky-p emacs)
()
user> (move-window-to emacs 200 100)
#<window 1e00011>
user> (window-absolute-position emacs)
(200 . 100)


O Manual de Programação do Sawfish documenta a API para programação e extensão do gerenciador de janelas.

Abaixo está um screenshot com temas pouco usuais do sawfish (cada janela pode ser decorada com um tema diferente).



A seguir está um exemplo de aplicação feita na linguagem de extensão do Sawfish. Os aplicativos no canto superior esquerdo da tela são dockapps (aqui há vários deles). Quando um dockapp é executado em um gerenciador de janelas sem suporte a dock, o aplicativo é mostrado como uma janela normal, com bordas, decorações e respondendo a eventos como qualquer outra janela. Quando o gerenciador de janelas oferece suporte a dockapps, ou quando se usa um programa externo para gerenciar dockapps, eles são mostrados como no screenshot e têm comportamento diferenciado (são exibidos em todas as áreas de trabalho, não são cobertos por outras janelas quando elas são maximizadas etc).



A extensão para gerenciamento de dockapps pode ser obtida em http://paginas.ucpel.tche.br/~mario/english/utils/sawdock/.

Sexta-feira, 24 de Abril de 2009

Criptografia simples (ingênua)

A seguir está um esquema simples de criptografia para autenticação em páginas web. Não é um esquema seguro e não envolve chaves públicas (criptografia de chaves assimétricas).

O esquema consiste em apresentar para o usuário uma matriz de números aleatórios (puzzle) para que ele possa dali extrair o texto a ser enviado para ser validado. A cada acesso à pagina uma matriz aleatória nova é exibida. Somente o usuário e o software para valição de senha sabem o segredo (chave) para determinar se a senha está ou não correta.

Na implementação mostrada a seguir (em Chicken Scheme), o código para valição de senha verifica se:

  • a senha possui quatro caracteres

  • o primeiro caractere da senha é igual ao caractere do canto superior esquerdo da matriz de números

  • o segundo caractere da senha é igual ao caractere do canto superior direito da matriz de números

  • o terceiro caractere da senha é igual ao caractere do canto inferior esquerdo da matriz de números

  • o quarto caractere da senha é igual ao caractere do canto inferior direito da matriz de números





Ou seja, o validador verifica se a senha corresponde aos números dos quatro cantos da matriz números concatenados no sentido horário, começando pelo canto superior esquerdo.

Obviamente o validador de senha é dos mais simples, mas poderia usar técnicas mais elaboradas como, por exemplo, o código de Beale. Neste caso, o validador teria um texto padrão, o qual seria de conhecimento do usuário, e os números da matriz indicariam a posição das letras no texto. Assim, o usuário digitaria as letras correspondentes às posições do texto indicadas em lugares pré-determinados da matriz (o uso da matriz é uma otimização sobre o código de Beale -- originalmente seriam apresentados apenas os números indicando a posição das letras no texto).

O código de servidor web (extensão spiffy de Chicken) está abaixo (web-server.scm):

#!/usr/bin/csi -script

(use spiffy spiffy-utils web-scheme web-scheme-handler (srfi 1 13))

(spiffy-file-ext-handlers `(("ws" . ,web-scheme-handler)))
(spiffy-tcp-port 8080)
(spiffy-root-path "./")

(start-server)


A implementação do esquema de autenticação está a seguir (index.ws):

(define (make-puzzle)
;; Retorna uma lista de listas de numeros aleatorios entre 0 e 9
;; (inclusive 0 e 9).
(let* ((line-length 40)
(num-columns 10)
(random-line
(lambda ()
(map (lambda (_) (random 10))
(iota line-length)))))
(map (lambda (_)
(random-line))
(iota num-columns))))

(define (valid-passwd? passwd puzzle)
;; Chave (ingenua): a senha corresponde aos numeros dos cantos da
;; tabela de numeros concatenados no sentido horario, comecando pelo
;; canto superior esquerdo.

(define ($ pos)
;; Retorna o caractere da posicao POS em PASSWD, ou #f se POS nao
;; existir em PASSWD.
(handle-exceptions exn #f (->string (string-ref passwd pos))))

(define (check pw-pos key-pos)
(equal? ($ pw-pos) (number->string key-pos)))

(and passwd
(= (string-length passwd) 4)
(check 0 (caar puzzle))
(check 1 (last (car puzzle)))
(check 2 (car (last puzzle)))
(check 3 (last (last puzzle)))))

(define (auth-page #!optional preamble)
;; Pagina de autenticacao.
(ws:page
(let ((puzzle (make-puzzle)))
(center
(if preamble (p preamble) "")
(ws:make-table puzzle)
(form 'method "post"
(input 'type "hidden" 'name "puzzle"
'value (with-output-to-string (cut pp puzzle)))
(input 'type "password" 'name "passwd")
(input 'type "submit"))))))

(let ((passwd (post-var "passwd"))
(puzzle (post-var "puzzle")))
(if (and puzzle passwd)
(if (valid-passwd? passwd (with-input-from-string puzzle read))
(ws:page "Senha correta")
(auth-page "Senha errada!"))
(auth-page)))

Camiseta do projeto Chicken!

Há umas semanas, Felix Winkelmann, autor e principal desenvolvedor do projeto Chicken Scheme, gentilmente me enviou um e-mail informando que me mandaria uma camiseta do projeto Chicken. Hoje, para a minha felicidade, recebi a camiseta. Muito bonita. :-)



Um tempo atrás tentei fazer uma camiseta dessas no site http://www.camisaonline.com.br/ mas fui roubado. Efetuei o pagamento e nunca recebi a camiseta nem resposta para os vários e-mails que enviei para o endereço de contato. Não comprem desse site.

Segunda-feira, 30 de Março de 2009

Definição de procedimentos e return explícito

A seguir está um pequeno hack em Chicken Scheme usando procedimento de escape (via call/cc) e definição de macros ao estilo de Common Lisp. No final das contas, tem-se definições de função como em Python ou Ruby, incluindo return explícito (para os masoquistas).

(define-macro (def procname args . body)
`(define (,procname ,@args)
(call/cc (lambda (return)
,@body))))


Exemplo de uso (considerando programadores de linguagens cujo if não passa seu resultado para a sua continuação):

(def fatorial(n)
(if (< n 2)
(return 1)
(return (* n (fatorial (sub1 n))))))


Exemplo de uso (considerando um schemer sendo obrigado a usar return):

(def fatorial(n)
(return (if (< n 2)
1
(* n (fatorial (sub1 n))))))


Código equivalente em Scheme puro:

(define (fatorial n)
(if (< n 2)
1
(* n (fatorial (sub1 n)))))

Terça-feira, 9 de Dezembro de 2008

Americanas: "inconsistências"

Há umas semanas tentei comprar um notebook das Americanas. Um dia após eu ter feito o pedido, recebo um e-mail pedindo para enviar dados a serem avaliados pela instituição financeira responsável pelo meu cartão de crédito. No mesmo dia respondi o e-mail com as informações solicitadas (com as mesmas informações que usei em compras feitas nas Americanas anteriormente).

Dois dias depois recebo um e-mail indicando que as informações haviam sido recebidas e que a análise demoraria até 36h. Depois de mais dois dias recebo um e-mail informando que o pedido havia sido suspenso devido a inconsistência de informações na sua compra e também à ausência de contato nossa Central de Atendimento. Ressalto que respondi todas as requisições de contato e pedidos de informações que a mim chegaram.

Via e-mail, tentei obter informações sobre a razão da suspensão do pedido, mas não obtive resposta.

Hoje fui ver a fatura do meu cartão de crédito e percebi que o valor da parcela da compra havia sido debitado. É claro, ficou a dúvida: se as informações eram inconsistentes para efetuar a compra, por que não eram inconsistentes para fazer a cobrança?

Tentei fazer esta pergunta para o atendimento online das Americanas, via chat. A conversa está abaixo (fui fazendo a captura das telas conforme a conversa se desenvolvia, pois temia que o texto fosse desaparecer quando o atentente desejasse -- o que de fato aconteceu).
















Não consegui capturar a última tela, pois o atendente "desligou".

Fica o conselho: cautela ao comprar das Americanas.