SGCG

…esto no es un subtítulo…

Ir a: contenido categorías calendario archivo suscripción

Volver arriba

Julio de 2015

Calendario de artículos de de 2015

lumamijuvido
12345
6789101112
13141516171819
20212223242526
2728293031

Palabras curiosas (11): «sicofanta»

2015-07-31

Continuamos con nuestra serie de artículos dedicados a palabras raras, llamativas o divertidas. La palabra de hoy es «sicofanta», una palabra rimbombante como pocas que hace referencia a una persona que acusa falsamente a otra de algo: un calumniador. La palabra viene del griego «συκοφάντης», algo así como «revelador de higos», pero inmediatamente referido a las personas que se aprovechan del sistema legal acusando falsamente. Hay varias hipótesis sobre qué tiene que ver el tema de los higos con esto: quizá se refiere a quienes acusaban de robo o contrabando de higos para obtener una recompensa o quizá se refiere a un gesto obsceno hecho por una parte acusadora excesivamente impetuosa.

Es interesante que la palabra inglesa «sycophant» no se refiere a los calumniadores, sino a los que adulan falsamente. Hay un tema común: ambos significados tratan sobre personas que actúan con falta de sinceridad, posiblemente para obtener un beneficio injusto.


Categorías: Lingüística

Permalink: https://sgcg.es/articulos/2015/07/31/palabras-curiosas-11-sicofanta/

Idea: zapatero hecho con cajas de galletas

2015-07-29

La típica caja de cartón de 2 kg de galletas María tiene el tamaño adecuado para almacenar tres zapatos. Dos cajas puestas una junto a la otra, por lo tanto, sirven para almacenar tres pares de zapatos con la mínima inconveniencia de que uno de ellos queda dividido por el tabique central formado por las paredes contiguas de las cajas. Con suficientes cajas apiladas y unidas con cinta de doble cara, sale un mueble zapatero humilde, pero funcional, que permite aprovechar bien el espacio en un armario o una estantería.

Zapatero de bajo coste.
Zapatero de bajo coste.


Categorías: DIY

Permalink: https://sgcg.es/articulos/2015/07/29/idea-zapatero-hecho-con-cajas-de-galletas/

Emacs Lisp en casos prácticos (9): ejecutar un programa externo periódicamente (3)

2015-07-26

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

El monitor en acción.
El monitor en acción.

En el artículo de hoy, continuación del anterior, creamos unas funciones para ejecutar un programa externo periódicamente y mostrar su salida en un búfer: una réplica de las herramientas watch de GNU/Linux, cmdwatch de FreeBSD y gnuwatch de OpenBSD.

El código de este artículo funciona en GNU Emacs 24.4.

Hoy terminamos el trabajo empezado en el artículo anterior.

Cancelación del temporizador al cerrar el búfer

Ayer quedó por describir la función hook que se ejecuta al cerrar el búfer y que sirve para cancelar el temporizador en marcha. Es muy sencilla:

(defun watch--kill-buffer-hook () "Cancel the currently running timer when killing the buffer." (when watch--timer (cancel-timer watch--timer)))

Ejecución del programa externo

El temporizador watch--timer que creamos ayer se encarga de llamar a la función watch--calback cada vez que salta el tiempo de espera. Esta función recibe como argumento el búfer en el que tiene que trabajar y lo primero que hace es elegir tal búfer, ya que Emacs puede estar operando en cualquier otro sitio en el momento en el que salta el temporizador. Es así:

(defun watch--callback (buffer) "Callback function to be executed by Watch mode's timer. To be executed on Watch's BUFFER." (with-current-buffer buffer (let ((inhibit-read-only t)) (erase-buffer) (watch--insert-header-text)) (setq watch--process (start-process-shell-command watch--command buffer watch--command)) (set-process-filter watch--process 'watch--filter) (set-process-sentinel watch--process 'watch--sentinel) (let ((inhibit-read-only t)) (watch--show-command-state))))

Analicémosla punto por punto:

Cabecera y notificación del estado del programa externo

La cabecera contendrá la fecha en la que se ordenó la ejecución del programa externo (obtenida con current-time-string), el intervalo de tiempo entre ejecuciones (nuestra variable watch--interval), la orden con la que se ejecuta el programa externo (nuestra variable watch--command) y el estado de la ejecución del programa externo. Este último dato lo escribiremos con la función watch--show-command-state, mientras que lo primero irá con watch--show-header-text:

(defun watch--insert-header-text () "Insert the header for the current watch process." (let ((text (format "%s\nEvery %g s: %s\n\n\n" (current-time-string) watch--interval watch--command))) (insert (propertize text 'face 'bold)))

(defun watch--show-command-state () "Show the state of the currently Watched command." (save-excursion (goto-char (point-min)) (forward-line 2) (delete-region (progn (beginning-of-line) (point)) (progn (end-of-line) (point))) (let ((text (format "Command state: %s" (process-status watch--process)))) (insert (propertize text 'face 'bold)))))

Lo interesante de estas funciones es que escriben en texto en negrita gracias al uso de propertize.

La función watch--show-command-state mueve el punto de un lado para otro internamente, así que protege la posición inicial con save-excursion.

Función filtro

La función filtro watch--filter se limita a escribir en el búfer la salida del programa externo:

(defun watch--filter (process string) "Filter function for the output STRING of Watch mode's running command PROCESS." (let ((buffer (process-buffer process))) (when (buffer-live-p buffer) (with-current-buffer buffer (let ((inhibit-read-only t)) (save-excursion (goto-char (point-max)) (insert string)))))))

La función recibe como argumentos el proceso (process) y la salida más reciente del programa (string). Obtiene el búfer de trabajo, que es el asociado al proceso, con process-buffer. Solamente si el búfer está vivo (buffer-live-p), hace lo siguiente: selecciona este búfer con with-current-buffer porque Emacs podría estar en cualquier otro lugar. Después, inhibe la protección de escritura (afirma la variable inhibit-read-only y escribe el texto (con insert) al final del búfer (goto-char a la salida de point-max, que es la posición final, tal como indica la documentación de end-of-buffer que habría que hacer en los programas escritos en Emacs Lisp). La función mueve el punto internamente; para evitar que estos movimientos queden reflejados tras ejecutar la función, la posición está protegida con save-excursion.

Función centinela

La función centinela watch--sentinel se ejecuta cuando el proceso del programa externo cambia de estado. La función recibe como argumentos el proceso y el estado nuevo. La lógica inicial es similar a la de watch--filter, pero no escribe texto del programa, sino que se limita a actualizar la línea de estado con watch--show-command-state.

(defun watch--sentinel (process state) "Sentinel function for Watch. Run when the Watched PROCESS changes its STATE." (let ((buffer (process-buffer process))) (when (buffer-live-p buffer) (with-current-buffer buffer (let ((inhibit-read-only t)) (watch--show-command-state))))))

El código completo

El modo completo está disponible para descarga en el siguiente enlace: watch-mode.el


Categorías: Informática

Permalink: https://sgcg.es/articulos/2015/07/26/emacs-lisp-en-casos-practicos-9-ejecutar-un-programa-externo-periodicamente-3/

Emacs Lisp en casos prácticos (8): ejecutar un programa externo periódicamente (2)

2015-07-25

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

En el artículo de hoy, continuación del anterior, creamos unas funciones para ejecutar un programa externo periódicamente y mostrar su salida en un búfer: una réplica de las herramientas watch de GNU/Linux, cmdwatch de FreeBSD y gnuwatch de OpenBSD.

El código de este artículo funciona en GNU Emacs 24.4.

El problema

El código del artículo anterior tiene algunas deficiencias:

Además de esto, puede ser interesante mostrar alguna información adicional en la parte superior del búfer: el momento en el que se ejecutó por última vez el programa externo, el tiempo que transcurre entre ejecuciones, qué programa se ejecuta y en qué estado se encuentra la ejecución actual. Finalmente, como queremos poder detener y reanudar la monitorización (la ejecución periódica del programa externo), también es conveniente mostrar si estamos en pausa o en acción; la línea de modo es un buen lugar para mostrar esta información.

La solución drástica: trabajemos a nivel más bajo

En vez de hacer malabarismos alrededor de async-shell-command (la función que utilizábamos para ordenar la ejecución del programa externo), vamos a usar una función de un nivel un poco más bajo, start-process-shell-command, que nos permite un control más detallado de lo que estamos haciendo.

Vamos a crear un modo mayor nuevo, watch-mode, que recogerá la funcionalidad.

Variables especiales

Aunque no es la forma más limpia de trabajar, vamos a definir 4 variables ligadas al búfer en el que se ejecuta nuestro watch y que servirán para recoger el estado del modo:

watch--command
la orden a ejecutar;
watch--interval
el intervalo de tiempo entre ejecuciones;
watch--process
el proceso en el que se ejecuta la orden externa;
watch--timer
el temporizador que se encarga de ordenar la ejecución periódica.

(defvar watch--command nil "Shell command executed by Watch.") (defvar watch--interval 0 "Number of seconds Watch will wait between command executions.") (defvar watch--process nil "Currently running Watch process.") (defvar watch--timer nil "Currently running Watch timer.") (make-variable-buffer-local 'watch--command) (make-variable-buffer-local 'watch--interval) (make-variable-buffer-local 'watch--process) (make-variable-buffer-local 'watch--timer)

Función principal

Crearemos un búfer de monitorización igual que antes: con la función interactiva watch. El código es ahora muy sencillo gracias a que la lógica está encapsulada en otras funciones:

(defun watch (command interval) "Run shell COMMAND every INTERVAL seconds showing the results in a dedicated buffer." (interactive (list (read-shell-command "Shell command: " nil nil) (read-number "Update interval (seconds): "))) (let* ((buffer-name (format "*Watch: %s*" command)) (buffer (generate-new-buffer buffer-name))) (switch-to-buffer buffer)) (watch-mode) (watch--start command interval) (add-hook 'kill-buffer-hook 'watch--kill-buffer-hook nil t))

Lo que hacemos es crear un búfer nuevo (igual que antes), ponerlo en nuestro nuevo modo watch-mode. Seguidamente, hacemos todas las labores de arranque del proceso de monitorización con una función que llamaremos watch--start. Finalmente, añadimos una función hook a kill-buffer-hook para cancelar el temporizador cuando se cierra el búfer.

El nuevo modo mayor watch-mode deriva de special-mode, un modo que está pensado para contenido generado dinámicamente y que no es texto que queramos editar. La declaración de nuestro modo se encarga únicamente de asignar algunas órdenes del teclado:

(define-derived-mode watch-mode special-mode "Watch" "Major mode for executing periodically an external shell command." (local-set-key (kbd "i") 'watch-set-interval) (local-set-key (kbd "p") 'watch-pause) (local-set-key (kbd "r") 'watch-resume))

El modo special-mode hace que el búfer sea de solo lectura. Evidentemente, como tenemos que llenarlo con la salida del programa externo, habrá que hacer algo especial; lo veremos mañana.

Arranque

La función watch--start, que arranca el temporizador, hace un trabajo similar al que realizaba directamente la versión de watch del artículo anterior:

(defun watch--start (command interval) "Start Watching (periodically running a COMMMAND every INTERVAL seconds)." (interactive) (setq watch--command command) (setq watch--interval interval) (setq watch--timer (run-with-timer 0 interval 'watch--callback (current-buffer))) (setq mode-line-process ": Running"))

Las principales novedades son el uso de las variables que declaramos antes y la escritura del texto ": Running" en la línea de modo.

Control de la monitorización

La función interactiva watch-pause sirve para detener la monitorización. Solamente tiene sentido pausar una monitorización en marcha; indicamos que la monitorización se ha detenido asignando un valor nulo al temporizador watch--timer después de detenerlo. Tras detener el temporizador, mostramos el estado de pausa de la monitorización en la línea de modo (asignamos el valor ": Paused" a mode-line-process) y redibujamos con redraw-display para forzar que se muestre el cambio que hemos hecho, ya que a menudo puede no actualizarse la pantalla. Finalmente, matamos el proceso watch--process si está activo. El código es el siguiente:

(defun watch-pause () "Stop Watching (periodically running a command)." (interactive) (when watch--timer (cancel-timer watch--timer) (setq watch--timer nil) (setq mode-line-process ": Paused") (redraw-display)) (when (and watch--process (not (process-live-p watch--process))) (kill-process watch--process)))

La función interactiva watch-resume sirve para reanudar la ejecución del temporizador y es muy sencilla, ya que se limita a llamar a watch--start. Como solamente tiene sentido reanudar la ejecución cuando está detenida, nos fijamos en el valor del temporizador watch--timer: si es nulo, la monitorización está en pausa y podemos reanudar.

(defun watch-resume () "Resume Watching (periodically running a command)." (interactive) (unless watch--timer (watch--start watch--command watch--interval)))

Cambiamos el intervalo entre ejecuciones con la función interactiva watch-set-interval, que acepta un número de segundos, pausa la monitorización, asigna el nuevo intervalo y reanuda la monitorización:

(defun watch-set-interval (seconds) "Set the interval SECONDS between command executions in the current Watch buffer." (interactive "nInterval (seconds): ") (watch-pause) (setq watch--interval seconds) (watch-resume))

Mañana, más

Falta poco trabajo para tener terminado el monitorizador de programas externos watch. Mañana veremos cómo ejecutar el programa externo, qué hacer con su salida y qué hacer cuando se detiene su ejecución.


Categorías: Informática

Permalink: https://sgcg.es/articulos/2015/07/25/emacs-lisp-en-casos-practicos-8-ejecutar-un-programa-externo-periodicamente-2/

Emacs Lisp en casos prácticos (7): ejecutar un programa externo periódicamente (1)

2015-07-23

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

En el artículo de hoy, crearemos una función para ejecutar un programa externo periódicamente y mostrar su salida en un búfer: una réplica de las herramientas watch de GNU/Linux, cmdwatch de FreeBSD y gnuwatch de OpenBSD. Vamos a usar temporizadores, así que el gestor de temporizadores del último artículo puede ayudarnos durante el desarrollo.

El código de este artículo funciona en GNU Emacs 24.4.

El problema

Las herramientas watch, cmdwatch y gnuwatch sirven para ejecutar periódicamente una orden y mostrar su salida a pantalla completa en una consola. Queremos hacer algo equivalente en Emacs: ejecutar periódicamente un programa externo y mostrar su última salida en un búfer.

Plan a seguir

Vamos a construir una función interactiva que preguntará al usuario qué orden ejecutar y cada cuánto tiempo hacerlo. Después, la función creará un temporizador (con run-with-timer) que se ocupará de ejecutar nuestro programa externo de forma asíncrona con async-shell-command. La salida irá en un búfer con el nombre *Watch: PROGRAMA* (donde PROGRAMA es el programa externo que hemos mandado ejecutar); si eliminamos este búfer, ya no tiene sentido que se ejecute el temporizador, así que lo eliminaremos con una función hook añadida a kill-buffer-hook (para que se ejecute al matar el búfer).

Implementación

La función, que acepta la orden a ejecutar (command) y los segundos entre ejecuciones (interval), es así:

(defun watch (command interval) "Run shell COMMAND every INTERVAL seconds showing the results in a dedicated buffer." (interactive (list (read-shell-command "Shell command: " nil nil) (read-number "Update interval (seconds): "))) (let* ((buffer-name (format "*Watch: %s*" command)) (buffer (generate-new-buffer buffer-name))) (switch-to-buffer buffer) (lexical-let ((timer (run-with-timer 0 interval 'watch--callback buffer command))) (add-hook 'kill-buffer-hook (lambda () (cancel-timer timer)) nil t))))

Analicémosla punto por punto:

La función watch--callback, que es la que se ejecuta cada vez que salta el temporizador, lo único que hace es llamar a la orden con async-shell-command:

(defun watch--callback (buffer command) (async-shell-command command buffer))ime

Esto tiene el problema de que se muestra el búfer cada vez que salta el temporizador, lo que puede ser molesto. Para evitarlo, hacemos lo que recomienda la documentación de async-shell-command:

(push '("^\*Watch: .*\*" display-buffer-no-window) display-buffer-alist)

Ejecutamos esto solamente una vez. El elemento que hemos añadido a display-buffer-alist es una lista con dos campos: una expresión regular que casa con el esquema de nombres que hemos usado para nuestros búferes y la función display-buffer-no-window, que se encarga de que no se muestre el búfer si ya está oculto.

Problemas pendientes

Cada vez termina la ejecución del programa externo con async-shell-command, aparece un mensaje en el minibúfer. Esto puede ser muy molesto, así que buscaremos una forma de evitarlo.

No estamos permitiendo una forma sencilla de parar el temporizador o cambiar el intervalo entre ejecuciones. Podemos usar nuestro nuevo y flamante gestor de temporizadores, pero es mejor ofrecer esa funcionalidad directamente.


Categorías: Informática

Permalink: https://sgcg.es/articulos/2015/07/23/emacs-lisp-en-casos-practicos-7-ejecutar-un-programa-externo-periodicamente-1/

Emacs Lisp en casos prácticos (6): gestor de temporizadores (2)

2015-07-20

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

Menú de temporizadores.
Menú de temporizadores.

En el artículo de hoy, continuación del de ayer, crearemos un modo mayor que nos servirá para gestionar temporizadores, algo que es útil si vamos a desarrollar algún código que los use.

Ayer creamos un modo mayor derivado de tabulated-list-mode, timer-menu-mode, que sirve para mostrar y gestionar temporizadores (unos elementos que sirven para ejecutar tareas tras cierto tiempo, opcionalmente de forma periódica). La definición del modo hace refencia a varias funciones que hoy definiremos.

Llenado de la tabla de temporizadores

Ayer hicimos que la tabla de temporizadores se actualizara con la función timer-menu--refresh que añadimos como función hook a tabulated-list-revert-hook. Lo único que tiene que hacer nuestra función timer-menu--refresh es actualizar el contenido de tabulated-list-entries:

(defun timer-menu--refresh () "Recompute the list of timers for the Timer Menu buffer." (setq tabulated-list-entries (timer-menu--entries)))

¡Así, llamando a otra función (timer-menu--entries) para generar las entradas, es fácil! ¿Qué hace esta función? Lo siguiente:

(defun timer-menu--entries () "Compute the list of entries for the tabulated list." (mapcar (lambda (timer) (list timer (vector "" (format "%S" (timer--repeat-delay timer)) (format "%S" (timer--function timer))))) timer-list))

Lo que generamos es lo que dice la documentación de tabulated-list-entries: una lista cuyos elementos son a su vez listas cuyo primer elemento es el identificador de la entrada y cuyo segundo elemento es un vector con el texto de cada celda que hay que mostrar. En nuestro caso, construimos la lista con mapcar sobre timer-list (que contiene los temporizadores vivos en este momento); para cada temporizador, construimos una entrada: el propio temporizador actúa como identificador y las celdas son un texto vacío (que se transformará en «D» en las entradas que marquemos para cancelar), el tiempo entre repeticiones (obtenido con timer--repeat-delay) y la función a llamar cuando el temporizador salta (obtenida con timer--function).

Cancelación de temporizadores

El código es similar al que creamos en el artículo en el que añadimos funciones de eliminación de subprocesos a process-menu-mode. Podemos marcar temporizadores para cancelar (con lo que aparecen con la marca «D»), desmarcarlos (con lo que la marca desaparece) y cancelar los temporizadores marcados. El código es casi idéntico al de aquel artículo y sería razonable sacar la funcionalidad común y meterla en una biblioteca. Sin más explicación, ahí queda:

(defun timer-menu-mark-for-deletion () "Mark the timer on this Timer List buffer line for deletion." (interactive) (tabulated-list-set-col 0 "D" t) (forward-line))

(defun timer-menu-unmark () "Unmark the timer on this Timer List buffer line." (interactive "P") (tabulated-list-set-col 0 "" t) (forward-line))

(defun timer-menu-execute () "Delete the timers marked for deletion." (interactive) (dolist (entry tabulated-list-entries) (let ((timer (elt entry 0)) (mark (elt (elt entry 1) 0))) (if (string-equal "D" mark) (cancel-timer timer)))) (revert-buffer))

Cambio del tiempo de espera entre repeticiones

Sería fácil hacer una función interactiva que pidiera los segundos de espera entre repeticiones y los aplicara al temporizador elegido:

(defun timer-menu-set-repeat-delay (delay) "Set the repeat DELAY seconds of the selected timer." (interactive "nSeconds: ") (let ((timer (tabulated-list-get-id))) (when timer (timer-set-time timer (timer--time timer) delay) (revert-buffer))))

Esta función pide de forma interactiva un número de segundos con interactive. Después, obtiene el temporizador seleccionado (con tabulated-list-get-id, que devuelve el identificador de la entrada seleccionado; construimos las entradas para que los identificadores fueran los temporizadores correspondientes) y, si este temporizador no es nulo (si hay realmente una entrada seleccionada), modifica el retardo con timer-set-time y actualiza la tabla de temporizadores con revert-buffer.

Sería conveniente hacer la función un poquito más inteligente: que pida los segundos entre repeticiones solamente si hay una entrada seleccionada y que ofrezca como valor por defecto el tiempo asignado actualmente. Podemos hacer esto con una declaración de función interactiva un poco más complicada que construya la lista de argumentos de forma explícita:

(defun timer-menu-set-repeat-delay (timer delay) "Set the repeat DELAY seconds of the selected TIMER." (interactive (let ((id (tabulated-list-get-id))) (list id (if id (read-number "Seconds: " (timer--repeat-delay id)) nil)))) (when timer (timer-set-time timer (timer--time timer) delay) (revert-buffer)))

Añadimos un argumento adicional, timer, para poder pasar el temporizador elegido al cuerpo de la función y así evitar llamar a tabulated-list-get-id por segunda vez.

Función interactiva para abrir el gestor de temporizadores

El convenio para los modos derivados de tabulated-list-mode es que haya una función interactiva cuyo nombre empiece por list- y que sirva para crear un nuevo búfer con el modo activado. Vamos a hacer lo propio con la siguiente función, list-timers:

(defun list-timers () "Display a list of all the timers managed by Emacs." (interactive) (let ((buffer (get-buffer-create "*Timer List*"))) (with-current-buffer buffer (timer-menu-mode) (list-timers--refresh) (tabulated-list-print)) (display-buffer buffer)))

Código

El código está disponible en el siguiente enlace: timer-menu-mode.el


Categorías: Informática

Permalink: https://sgcg.es/articulos/2015/07/20/emacs-lisp-en-casos-practicos-6-gestor-de-temporizadores-2/

Emacs Lisp en casos prácticos (5): gestor de temporizadores (1)

2015-07-19

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

En el artículo de hoy y el artículo de mañana, crearemos un modo mayor que nos servirá para gestionar temporizadores, algo que es útil si vamos a desarrollar algún código que los use.

El problema

Los temporizadores ofrecen una manera de ejecutar funciones cuando transcurre cierto tiempo, quizá de forma periódica. La última versión publicada de GNU Emacs en el momento de escribir estas líneas dispone de gestores para cosas como búferes y subprocesos, pero no para temporizadores. Estaría bien tener una interfaz similar a la de list-buffers para hacer algunas gestiones mínimas sobre los temporizadores que está manejando Emacs. Como ejemplo y por no complicarnos mucho, vamos a ofrecer dos opciones de gestión:

Anatomía simplificada de un temporizador

Los temporizadores en Emacs son estructuras del tipo timer con varios campos. Uno de ellos, repeat-delay (accesible mediante timer--repeat-delay) es el tiempo de repetición expresado en segundos. Otro campo que podría interesarnos es time (accesible mediante timer--time): el momento de la próxima ejecución del temporizador; ahora bien, con el fin de mantener cierta sencillez, ignoraremos este campo. Lo que sí vamos a mostrar es el campo function (accesible mediante timer--function): la función que se ejecuta cuando el temporizador salta.

Si tenemos un temporizador llamado my-timer, accedemos a su tiempo de repetición mediante
(timer--repeat-time my-timer)
Algo similar puede hacerse para acceder a otros campos; por ejemplo, así podemos ver la función a la que llama el temporizador al saltar:
(timer--function my-timer)

Plan de acción: creación de un modo derivado de tabulated-list-mode

Emacs tiene varias funciones para mostrar tablas con listas de cosas abiertas: la lista de búferes abiertos (que se abre con la función interactiva list-buffers, C-x C-b) y la lista de subprocesos (que se abre con la función interactiva list-processes) son dos de ellas. Estas tablas se muestran con modos mayores derivados de tabulated-list-mode. Nuestra lista de temporizadores también puede aprovechar la funcionalidad ofrecida por tabulated-list-mode.

Lo primero que tenemos que hacer es requerir tabulated-list:

(require 'tabulated-list)

Para definir un modo mayor derivado de tabulated-list-mode, usamos define-derived-mode, que se encarga de hacer todo el trabajo rutinario que habría que hacer siempre para definir de un modo mayor derivado de otro y deja en nuestras manos el trabajo despecífico del modo. En nuestro caso, seguimos las instrucciones que aparecen en la documentación de tabulated-list-mode:

Antes de llegar al último paso, establecemos cómo ordenar la tabla (con la variable tabulated-list-sort-key) y asignamos las combinaciones de teclas (con local-set-key) para poder manejar el menú de temporizadores con el teclado. La definición del modo queda así:

(define-derived-mode timer-menu-mode tabulated-list-mode "Timer Menu" "Major mode for listing the timers managed by Emacs." (setq tabulated-list-format [("" 1 t) ("Repeat delay" 16 t) ("Function" 40 t)]) (setq tabulated-list-sort-key '("Function")) (local-set-key (kbd "\C-k") 'timer-menu-mark-for-deletion) (local-set-key (kbd "k") 'timer-menu-mark-for-deletion) (local-set-key (kbd "\C-d") 'timer-menu-mark-for-deletion) (local-set-key (kbd "d") 'timer-menu-mark-for-deletion) (local-set-key (kbd "u") 'timer-menu-unmark) (local-set-key (kbd "x") 'timer-menu-execute) (local-set-key (kbd "t") 'timer-menu-set-repeat-delay) (add-hook 'tabulated-list-revert-hook 'list-timers--refresh nil t) (tabulated-list-init-header))

Analicemos lo que este código hace:

Mañana veremos qué hacen las funciones asignadas a las combinaciones de teclas y la función que actualiza las entradas de la tabla.


Categorías: Informática

Permalink: https://sgcg.es/articulos/2015/07/19/emacs-lisp-en-casos-practicos-5-gestor-de-temporizadores-1/

Emacs Lisp en casos prácticos (4): cargar todos los ficheros de Emacs Lisp de un directorio

2015-07-16

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

Hoy vamos a crear unas funciones para cargar todos los ficheros de Emacs Lisp de un directorio. Muchos usuarios de Emacs tienen configuraciones complicadas que están repartidas en ficheros diferentes dedicado cada uno a cubrir cierta funcionalidad; en tales casos puede ser útil tener una función para cargar todo el código de un directorio.

El problema

La función load sirve para cargar un fichero de Emacs Lisp, pero no para cargar varios. Queremos crear una función nueva, load-directory, encargada de hacer esto. También puede ser interesante hacer que esta función trabaje de forma recursiva en un árbol de directorios.

Función de carga

Si asumimos que disponemos de una función llamada file-name-is-elisp? que determina si un nombre de fichero hace referencia a código de Emacs Lisp, la carga de todos los ficheros de Emacs Lisp de un directorio es fácil. Podríamos hacer lo siguiente:

(defun load-directory (directory) "Load the Emacs Lisp files in a DIRECTORY." (dolist (name (directory-files directory t)) (when (and (file-regular-p name) (file-name-is-elisp? name)) (load (file-name-sans-extension name)))))

Esta función lista el contenido de un directorio con directory-files, itera sobre esta lista y carga todos los ficheros regulares de Emacs Lisp (que cumplen file-regular-p y file-name-is-elisp?) y los carga con load. Para este paso de la carga, hay que usar el nombre de fichero sin extensión (lo que se consigue conj file-name-sans-extension).

La función anterior está bien, pero no es recursiva. Para cargar el contenido de un árbol de directorios de forma recursiva, añadimos un parámetro opcional y complicamos un poco la función:

(defun load-directory (directory &optional recursive) "Load the Emacs Lisp files in a DIRECTORY. Operate recursively in its subdirectories if RECURSIVE is non-nil." (dolist (name (directory-files directory t)) (if (not (string-match "/\\.[^/]*$" name)) (if (file-directory-p name) (when recursive (load-directory name t)) (when (file-name-is-elisp? name) (load (file-name-sans-extension name)))))))

La primera comprobación que hacemos, la de string-match, sirve para descargar entradas que empiezan con un punto, que en *nix son ficheros ocultos y también incluyen la entrada «.» (el directorio actual) y la entrada «..» (el directorio padre), con lo que es necesario dejar fuera estos elementos para procesar el árbol correctamente. Lo demás es muy similar, pero añadimos la posibilidad de operar de forma recursiva si nos encontramos con un subdirectorio (comprobado con file-directory-p). La comprobación file-regular-p queda fuera al ser casi complementaria con file-directory-p. Si realmente necesitáramos comprobar que trabajamos con ficheros regulares, podríamos añadir la instrucción correspondiente.

Si en un directorio hay algún fichero de Emacs Lisp presente tanto en su forma original como compilada, la función anterior lo carga varias veces. Para evitarlo, podemos llevar una lista de entradas vistas:

(defun load-directory (directory &optional recursive) "Load the Emacs Lisp files in a DIRECTORY. Operate recursively in its subdirectories if RECURSIVE is non-nil." (let ((visited nil)) (dolist (name (directory-files directory t)) (if (not (string-match "/\\.[^/]*$" name)) (if (file-directory-p name) (when recursive (load-directory name t)) (when (file-name-is-elisp? name) (let ((file (file-name-sans-extension name))) (unless (member file visited) (push file visited) (load file)))))))))

Comprobar si un nombre de fichero es de Emacs Lisp

Queda escribir la función file-name-is-elisp?. Esta función es muy fácil: extrae la extensión (con file-name-extension) del fichero que se le pasa y la busca en load-suffixes, que contiene las extensiones (con punto delante, así que como file-name-extension devuelve la extensión sin punto, habrá que añadirlo para poder comparar). La función es así:

(defun file-name-is-elisp? (file-name) "Say if the extension of a FILE NAME implies Emacs Lisp code. This is done by checking if the extension is present in `load-suffixes'." (let ((file-suffix (concat "." (downcase (file-name-extension file-name)))) (emacs-lisp-suffixes (mapcar 'downcase load-suffixes))) (if (member file-suffix emacs-lisp-suffixes) t nil)))

Hacemos las comparaciones sin distinguir entre mayúsculas y minúsculas; de ahí las llamadas a downcase.

Uso

Digamos que tenemos un árbol de directorios repleto de código de Emacs Lisp en ~/.emacs.d/custom/. Podemos cargar todo este código dentro de ~/.emacs.d/custom/ y sus subdirectorios con la siguiente orden que podría ir dentro de nuestro ~/.emacs.d/init.el:

(load-directory "~/.emacs.d/custom/" t)


Categorías: Informática

Permalink: https://sgcg.es/articulos/2015/07/16/emacs-lisp-en-casos-practicos-4-cargar-todos-los-ficheros-de-emacs-lisp-de-un-directorio/

Emacs Lisp en casos prácticos (3): cálculo de la letra del NIF (2)

2015-07-14

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

En el artículo anterior y el presente artículo vemos la forma de completar cómo calcular el dígito de control de un NIF (como la letra del DNI) y completar un texto.

El problema

Las funciones para calcular el dígito de control del NIF del artículo anterior estaban incompletas: no cubrían los NIF de tipo CIF. Un CIF empieza por una letra mayúscula (a excepción de la «Ñ», la «X», la «Y» y la «Z»), sigue con 7 dígitos numéricos y termina con el dígito de control, que es un número o una letra en función de la letra inicial. El cálculo del dígito de control es más complicado que en el caso del DNI y el NIE; se hace de la manera siguiente:

  1. Se coge la parte numérica (los 7 dígitos anteriores al dígito de control).
  2. De este número, se calcula la suma de los 3 dígitos que ocupan las posiciones pares (el segundo, el cuarto y el sexto empezando por la izquierda).
  3. Del número del primer paso, se calculan los dobles de los dígitos de las posiciones impares (empezando por la izquierda, el doble del primer dígito, el doble del tercer dígito, el doble del quinto dígito y el doble del séptimo dígito).
  4. Se calcula la suma de todas las cifras que resultan del paso anterior.
  5. Se toma la cifra de las unidades de la suma del resultado del paso anterior y el resultado del segundo paso.
  6. Se calcula la diferencia entre el número 10 y el número calculado en el paso anterior. Si la diferencia es 10, se toma el valor 0.
  7. Si el CIF empieza por la letra «N», por la letra «P», por la letra «Q», por la letra «R», por la letra «S» o por la letra «W», el dígito de control es la letra correspondiente al número calculado en el paso anterior en la siguiente lista:
    0
    J
    1
    A
    2
    B
    3
    C
    4
    D
    5
    E
    6
    F
    7
    G
    8
    H
    9
    I
  8. Si es otra letra, entonces el dígito de control es directamente el número calculado en el sexto paso.

Vamos a completar nuestro código para calcular el dígito de control de un CIF.

Cálculo del dígito de control

Asumamos que disponemos de dos funciones:

nif--digitos
acepta un número y devuelve una lista de números con los dígitos correspondientes a la entrada;
nif--sumar-digitos
acepta un número y devuelve la suma de sus dígitos.

Con estas dos funciones, calcular el dígito de control es una mera aplicación directa del algoritmo expresado arriba:

(defun nif-digito-cif (cif) "Devuelve el dígito de control correspondiente a un CIF." (let* ((inicial (elt cif 0)) (digitos (nif--digitos (substring cif 1))) (pares (list (nth 1 digitos) (nth 3 digitos) (nth 5 digitos))) (impares (list (nth 0 digitos) (nth 2 digitos) (nth 4 digitos) (nth 6 digitos))) (suma-pares (apply '+ pares)) (dobles-impares (mapcar (lambda (numero) (* 2 numero)) impares)) (sumas-impares (mapcar 'nif--sumar-digitos dobles-impares)) (suma-impares (apply '+ sumas-impares)) (suma (+ suma-pares suma-impares)) (unidades (car (last (nif--digitos suma)))) (digito (mod (- 10 unidades) 10))) (case inicial ((?N ?P ?Q ?R ?S ?W) (substring "JABCDEFGHI" digito (1+ digito))) (otherwise (number-to-string digito)))))

Funciones auxiliares

Para la función nif--digitos que devuelve la lista de dígitos de un número, lo que hacemos es convertir el número a una cadena de texto (con number-to-string) de la que es fácil separar los dígitos corresopndientes (con split-string):

(defun nif--digitos (numero) "Devuelve la lista de dígitos de un NÚMERO." (mapcar 'string-to-number (split-string (number-to-string numero) "" t)))

Para la función de sumar dígitos nif--sumar-digitos, explotamos la función nif--digitos que acabamos de escribir:

(defun nif--sumar-digitos (numero) "Suma los dígitos de un NÚMERO." (apply '+ (nif--digitos numero)))

Integración de la función de cálculo del dígito de control de un CIF

Para poder usar las funciones nif-digito-de-control, nif-completar y nif-completar-region que desarrollamos en el artículo anterior, hace falta simplemente actualizar nif--calculador-del-digito-de-control para que devuelva la función para calcular el dígito de control de un CIF en los casos relevantes:

(defun nif--calculador-del-digito-de-control (nif) (cond ((not (= 8 (length nif))) 'nif-digito-nulo) ((string-match "[0-9]\\{8\\}" nif) 'nif-letra-dni) ((string-match "[XYZ][0-9]\\{7\\}" nif) 'nif-letra-nie) ((string-match "[ABCDEFGHIJKLMNOPQRSTUVW][0-9]\\{7\\}" nif) 'nif-digito-cif) (t 'nif-digito-nulo)))

Código

El código de este artículo y el anterior está disponible aquí: nif.el


Categorías: Informática

Permalink: https://sgcg.es/articulos/2015/07/14/emacs-lisp-en-casos-practicos-3-calculo-de-la-letra-del-nif-2/

Emacs Lisp en casos prácticos (2): cálculo de la letra del NIF (1)

2015-07-08

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

En el artículo de hoy y el siguiente artículo veremos la forma de completar cómo calcular el dígito de control de un NIF (como la letra del DNI) y completar un texto.

El problema

El Número de Identificación Fiscal (NIF) es un código que identifica a efectos fiscales en España a las personas físicas y a las personas jurídicas. Este código es una combinación de 9 caracteres alfanuméricos (con las letras en mayúsculas) en la que el último carácter es un dígito de control que se calcula a partir de los 8 primeros caracteres. Hay diferentes tiposd de NIF, cada uno con su tipo de cálculo de dígito de control correspodiente:

DNI
Los 8 primeros caracteres de un NIF del tipo DNI son números y el dígito de control es una letra que sale del resto de la división del número formado por las 8 primeras cifras entre 23. Este resto puede ser un número entre 0 y 22; a cada número le corresponde una letra diferente que es el dígito de control.
NIE
Se parece al DNI, pero el primer carácter es una letra («X», «Y» o «Z») y el dígito de control se calcula a partir del número formado al excluir la letra inicial.
CIF
El CIF empieza por una letra (diferente a las del NIE) y sigue un algoritmo más complicado que no veremos hoy.

El dígito de control del NIF y el CIF sale de la siguiente relación entre el resto y la letra correspondiente:

0
T
1
R
2
W
3
A
4
G
5
M
6
Y
7
F
8
P
9
D
10
X
11
B
12
N
13
J
14
Z
15
S
16
Q
17
V
18
H
19
L
20
C
21
K
22
E

Hoy buscamos calcular el dígito de control de un DNI o un NIE e incluso completar un código al que le falta su dígito.

Cálculo del dígito de control

La letra del DNI es fácil de calcular con la siguiente función. Esta función acepta un texto que consiste en los 8 primeros dígitos del DNI (es decir, el código a falta del dígito de control), convierte dicho texto a un número para calcular el resto, y finalmente devuelve la letra que corresponde al resto.

(defun nif-letra-dni (dni) "Devuelve la letra correspondiente a un número de DNI." (let ((table "TRWAGMYFPDXBNJZSQVHLCKE") (remainder (mod (if (stringp dni) (string-to-number dni) dni) 23))) (substring table remainder (1+ remainder))))

Para calcular la letra de un NIE, reutilizamos la función que hace el trabajo con el NIF, a la que le pasamos el NIE tras retirar la letra inicial:

(defun nif-letra-nie (nie) "Devuelve la letra correspondiente a un NIE." (nif-letra-dni (substring nie 1)))

Además de esto, vamos a definir una función que devuelve un dígito de control nulo (una cadena de texto vacía) para los casos en los que no podemos calcular un dígito de control real:

(defun nif-digito-nulo (nif) "")

Calculador genérico del dígito de control

Es conveniente tener una función capaz de discernir el tipo de NIF que se le pasa y calcular el dígito de control de acuerdo al algoritmo correspondiente. Para ello, imaginamos que disponemos de una función llamada nif--calculador-del-digito-de-control que acepta un NIF y devuelve la función que se encarga de calcular el dígito de control (con lo que tenemos definido ahora, la que corresponda entre nif-letra-dni, nif-letra-nie o nif-digito-nulo). Si disponemos de esta función, el trabajo de calcular el dígito de control es así de fácil:

(defun nif-digito-de-control (nif) "Obtiene el dígito de control de un NIF." (funcall (nif--calculador-del-digito-de-control nif) nif))

La función que nos devuelve el calculador del dígito de control puede basarse tanto en el tamaño del código que le pasamos (si no es de 8 caracteres, no vale) como en el propio contenido (cazado con expresiones regulares mediante string-match):

(defun nif--calculador-del-digito-de-control (nif) (cond ((not (= 8 (length nif))) 'nif-digito-nulo) ((string-match "[0-9]\\{8\\}" nif) 'nif-letra-dni) ((string-match "[XYZ][0-9]\\{7\\}" nif) 'nif-letra-nie) (t 'nif-digito-nulo)))

Completar un NIF

Lo último que nos falta es poder completar un NIF. Esto lo hacemos añadiendo el dígito de control (calculado con la función nif-digito-de-control definida arriba) al final del código:

(defun nif-completar (nif) "Completa un NIF añadiéndole el dígito de control que le corresponde." (concat nif (nif-digito-de-control nif)))

Digamos que usamos esta función con el DNI 12345678:

(nif-completar "12345678")

Obtenemos el siguiente resultado:

"12345678Z"

Finalmente, creamos una función interactiva para completar un NIF que hayamos seleccionado mientras editamos un texto. Esta función obtiene como argumentos las posiciones inicial y final de la región seleccionada (que obtenemos en la invocación interactiva mediante el código "r"), extrae el texto en esta región mediante buffer-substring-no-properties (que sirve para obtener el texto simple sin información que ahora no nos interesa como los tipos de letra y los colores) e inserta al final el dígito de control calculado con nuestra función nif-digito-de-control:

(defun nif-completar-region (start end) "Añade la letra al nif en la región marcada entre START y END." (interactive "r") (let ((nif (buffer-substring-no-properties start end))) (save-excursion (goto-char end) (insert (nif-digito-de-control nif)))))


Categorías: Informática

Permalink: https://sgcg.es/articulos/2015/07/08/emacs-lisp-en-casos-practicos-2-calculo-de-la-letra-del-nif-1/

El caso de Oracle contra Google y el copyright de las interfaces de programación (3)

2015-07-05

Esta noticia me llega un poco tarde, pero voy a exponerla por su importancia y relativa oscuridad.

Oracle (la compañía que está actualmente detrás del lenguaje de programación Java tras adquirir Sun Microsystems) y Google mantienen un conflicto judicial desde hace años con motivo de la interfaz de programación del lenguaje Java. Oracle afirma poseer el copyright sobre dicha interfaz y espera obtener rentas por el uso que le da Google. El sistema operativo Android, propiedad de Google, contiene bibliotecas informáticas implementadas independientemente de las de Java de Oracle, pero que exponen la misma interfaz de programación. Por lo tanto, se trata de una hipotética violación no del copyright no del Java de Oracle, sino del de la interfaz de programación que tiene Java. Si la interfaz de programación está sujeta a copyright, entonces no está permitido desarrollar de forma independiente un producto compatible con Java.

Hace 3 años, en 2012, el juez Alsup del Distrito Norte de California se pronunció sobre el copyright de la interfaz de programación de Java: tal copyright era inaplicable por inexistente. Oracle apeló y, en mayo de 2014, el Circuito Federal de los Estados Unidos revirtió la decisión californiana y estableció tanto que Oracle posee el copyright sobre la interfaz de programación de Java como que Google violó dicho copyright. Google elevó una petición al Tribunal Supremo de los Estados Unidos y, hace unos días, a finales de junio de 2015, dicho tribunal se negó a revisar la decisión del Circuito Federal. El litigio no ha terminado y Google tiene derecho a plantear una defensa sobre el concepto de uso justo («fair use» en inglés), pero esta estrategia de defensa, más complicada que un simple el copyright no se aplica aquí, es menos accesible para otras compañías y otros individuos con menos recursos que el gigante Google.

La situación es jugosa para unos pocos mastodontes y muy desagradable para todos los demás innovadores del sector informático.


Categorías: Actualidad, Informática

Permalink: https://sgcg.es/articulos/2015/07/05/el-caso-de-oracle-contra-google-y-el-copyright-de-las-interfaces-de-programacion-3/