;;;; -*- mode:emacs-lisp;coding:utf-8 -*- ;;;;****************************************************************************** ;;;;FILE: pjb-utilities.el ;;;;LANGUAGE: emacs lisp ;;;;SYSTEM: emacs ;;;;USER-INTERFACE: emacs ;;;;DESCRIPTION ;;;; ;;;; This module exports various utility functions. ;;;; ;;;;AUTHORS ;;;; Pascal J. Bourguignon ;;;;MODIFICATIONS ;;;; 2005-07-28 Added compiletime-cond ;;;; 2002-02-17 Added stderr and stdout parameters to printf. ;;;; 2001-11-30 Added process-with-id. ;;;; 2001-11-02 Added foreach, commented-out macros. ;;;; 199?-??-?? Creation. ;;;;BUGS ;;;;LEGAL ;;;; LGPL ;;;; ;;;; Copyright Pascal J. Bourguignon 1990 - 2011 ;;;; ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public ;;;; License as published by the Free Software Foundation; either ;;;; version 2 of the License, or (at your option) any later version. ;;;; ;;;; This library is distributed in the hope that it will be useful, ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;;; Lesser General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU Lesser General Public ;;;; License along with this library; if not, write to the Free Software ;;;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ;;;; ;;;;****************************************************************************** (provide 'pjb-utilities) (require 'pjb-strings) (require 'pjb-cl) (require 'forms) (require 'comint) (require 'calendar) (defmacro compiletime-cond (&rest clauses) (if (eval (first (first clauses))) `(progn ,@(rest (first clauses))) `(compiletime-cond ,@(rest clauses)))) (defun recursive-apply (atom-func a-list b-list) "Applies recursively the function atom-func on each and every pairs that can be found recursively in the two parallel structures a-list and b-list. Only the elements from a-list must be an atom to be passed to atom-func. Examples: (recursive-apply '+ '((1 2) (3 4)) '((1 0) (0 1))) ---> ((2 2) (3 5)) (recursive-apply (lambda (atom other) (cons atom other)) '(apple orange peach) '((red yellow green) (orange) (yellow white))) ---> ((apple red yellow green) (orange orange) (peach yellow white)) " (cond ((null a-list) nil) ((atom a-list) (apply atom-func (list a-list b-list))) (t (cons (recursive-apply atom-func (car a-list) (car b-list)) (recursive-apply atom-func (cdr a-list) (cdr b-list)))))) (defun padd (a b) (recursive-apply '+ a b)) (defun psub (a b) (recursive-apply '- a b)) (defun pmul (a b) (recursive-apply '* a b)) (defun pdiv (a b) (recursive-apply '/ a b)) (defun pmod (a b) (recursive-apply '% a b)) (defun pjb-or (&rest args) "or is not a true function..." (while (and (consp args) (not (car args))) (setq args (cdr args))) (car args)) (defun pjb-equal (a b) "An implementation of equal that prints out the atoms." (cond ((and (atom a) (atom b)) (printf :stdout "%S\n%S\n\n" a b) (equal a b)) ((and (consp a) (consp b)) (and (pjb-equal (car a) (car b)) (pjb-equal (cdr a) (cdr b)))) (t (printf :stdout "%S\n%S\n\n" a b) nil))) (defun pjb-diff (a b) " DO: Show deep differences between a and b. a and b should be lists of same structure (recursively). RETURN: Whether there are differences between a and b. " (cond ((and (atom a) (atom b)) (if (equal a b) nil (printf :stdout "%S\n%S\n\n" a b) t)) ((and (consp a) (consp b)) (let ((t1 (pjb-diff (car a) (car b))) (t2 (pjb-diff (cdr a) (cdr b)))) (or t1 t2))) (t (printf :stdout "%S\n%S\n\n" a b) t))) (defun pjb-struct-diff (a b &optional cmp) " DO: Compare two structures and return the differences. RETURN: A list structured like a and b containing nil where elements from a and b match or (cons elt-a elt-b) when elt-a and elt-b differ. NOTE: cmp is a function used to compare atoms ('eq, 'equal or whatever). Default is 'eq. " (unless cmp (setq cmp 'eq)) (cond ((and (atom a) (atom b)) (if (funcall cmp a b) nil (cons a b)) ) ((and (consp a) (consp b)) (cons (pjb-struct-diff (car a) (car b)) (pjb-struct-diff (cdr a) (cdr b)))) (t (cons a b)))) (defun ^ (x exp) "Computes x^exp = x to the power of exp." (cond ((< exp 0) (/ 1.0 (^ x (- exp)))) ((= exp 0) 1) ((= exp 1) x) ((integerp exp) (if (= (% exp 2) 0) (let ((x2 (^ x (/ exp 2)))) (* x2 x2)) (let ((x2 (^ x (/ (- exp 1) 2)))) (* x x2 x2)))) (t (exp (* (log x) exp))))) (defun float-precision (&optional base) "RETURN: the number of base digit available in floating point numbers. Default is base Ten." (setq base (+ 0.0 (or base 10.0))) (let ((number 1.0) (number+1 (1+ 1.0)) (precision 0)) (while (/= number number+1) (setq number (* base number) number+1 (1+ number) precision (1+ precision))) precision)) (defun float-to-base (number base) "DO: Convert a number value into a string contening the same value expressed into the given base. 1 out is current buffer ;; TODO: This should be the same as :stdout ! ((stringp kind) (setq out (current-buffer) format-args args write (lambda (s) (write-string s out)) )) ;; printf stderr -> (message in any case ;; but when interactive, message is small ! ((eq :stderr kind) (setq format-args (cdr args) write (lambda (s) (if noninteractive (write-string s 'external-debugging-output) (message s))) ;; (lambda (s) (message "%s" s)) )) ;; printf stdout -> (printf (current-buffer) ...) if interactive ;; -> (printf t ...) if batch ((eq :stdout kind) (setq format-args (cdr args) write (lambda (s) (write-string s (if noninteractive t (current-buffer)))) )) (t (apply 'printf *STANDARD-OUTPUT* (cdr args)) (return-from :print)) ;;; (setq out (car args) ;;; format-args (cdr args) ;;; write (lambda (s) (write-string s out)) ;;; )) ) ;;cond (funcall write (apply 'format format-args))))) (defun write-string (string &optional out) " SEE-ALSO: `write-char'. " (mapc (lambda (char) (write-char char out)) string) nil) (defun show (&rest x) "Insert the formated value X." (unless (= (point) (progn (beginning-of-line) (point))) (end-of-line) (insert "\n")) (insert (format ";; --> %S\n" (if (= 1 (length x)) (car x) x))) (if (= 1 (length x)) (car x) x)) (defmacro mshow (&rest expressions) "Message the formated value of each expression in `expressions'." `(progn ,@(mapcar (lambda (expr) `(message "%s -> %S" ',expr ,expr)) expressions))) (defmacro for (var init final &rest body) "Execute a simple for loop: (for i 1 10 (print i))." (let ((tempvar (make-symbol "max"))) `(let ((,var ,init) (,tempvar ,final)) (if (< ,var ,tempvar) (while (<= ,var ,tempvar) ,@body (setq ,var (+ ,var 1))) (while (>= ,var ,tempvar) ,@body (setq ,var (- ,var 1))))))) (defun today () "Returns the date of today in YYYY-MM-DD format." (let* ((date (calendar-current-date)) (month (nth 0 date)) (day (nth 1 date)) (year (nth 2 date))) (format "%04d-%02d-%02d" year month day))) (defun remove-parity-from-region () "Replace the region by the same 8-bit text with the parity bit set to 0 (7-bit)." (interactive) (let* ((text-8 (buffer-substring-no-properties (region-beginning) (region-end))) (len (length text-8)) (text-7 (make-string len ??)) (i 0)) (while (< i len) (aset text-7 i (% (aref text-8 i) 128)) (setq i (+ 1 i))) (delete-region (region-beginning) (region-end)) (insert text-7))) (defun reverse-lines (start end) "Reverse the order of the characters in each lines in the region. (Useful for hebrew)." (interactive "r") (let* ((text)(lines)(first)) (setq text (buffer-substring-no-properties start end)) (setq lines (split-string text "[\n]")) (setq first lines) (while lines (if (< 0 (length (car lines))) (setcar lines (apply 'string (reverse (string-to-list (car lines)))))) (setq lines (cdr lines))) (delete-region start end) (insert (unsplit-string first "\n")))) (defun kill-all-empty-buffers (&optional invisibles-too) "DO: Kills all empty buffers. In addition, when invisibles-too, kills the invisible buffers too." (interactive "P") (let ((buffers (buffer-list)) (buf)) (while buffers (setq buf (car buffers) buffers (cdr buffers)) (set-buffer buf) (when (or (= 0 (buffer-size)) (and invisibles-too (= 32 (string-to-char (buffer-name buf))))) (set-buffer buf) (set-buffer-modified-p nil) (kill-buffer buf) )) (sleep 0))) (defun process-with-id (pid) "RETURN: The process whose process-id is pid (or nil of none is found in (process-list))." (let ( (pl (process-list)) (pr) ) (while pl (if (= pid (process-id (car pl))) (setq pr (car pl) pl nil) (setq pl (cdr pl)))) pr)) (defun plist-remove (plist key) "RETURN: A new plist with the elements in plist but the one with key. NOTE: A suffix in result may be a suffix of plist too." (if (eq (car plist) key) (cdr (cdr plist)) (cons (car plist) (cons (cadr plist) (plist-remove (cddr plist) key))))) (defun seconds-to-emacs-time (secs) " PRE: secs is a number of seconds. RETURN: The time represented by secs in emacs time format, ie. a list ( h l us ) with h=secs/2^16, l=secs%2^16, us=(secs*1e6)%1e6. " (let* ( (h (truncate (/ secs 65536))) (lf (- secs (* h 65536.0))) (l (truncate lf)) (us (truncate (* 1000000.0 (- lf l)))) ) (list h l us))) (defun emacs-time-to-seconds (et) " PRE: et is a time in emacs time format, ie. a list ( h l us) with h and l being in [0..65535] and us in [0..999999]. RETURN: et expressed as a scalar." (+ (let ((h (nth 0 et))) (if (< h 1024) (* h 65536) (* h 65536.0))) (nth 1 et) (let ((us (nth 2 et))) (if (= 0 us) 0 (/ us 1000000.0))))) (defun get-ip-interfaces () " RETURN: A list of list of strings (interface-name ip-address up-or-down) ip-address and up-or-down (\"UP\" or \"DOWN\") may be absent. " (let ((l (mapcar (lambda (line) (cond ((string-match "^\\([^ ]+\\) .*" line) (match-string 1 line)) ((string-match "^ .* inet addr:\\([0-9\\.]+\\) .*" line) (match-string 1 line)) ((string-match "^ .* \\(UP\\|DOWN\\) .*" line) (match-string 1 line)) ((string-equal line "") :next) (t nil))) (split-string (shell-command-to-string "ifconfig -a") "\n")) ) (r nil) (i nil)) (while l (cond ((null (car l)) ;; nop ) ((eq :next (car l)) (setq r (cons (nreverse i) r)) (setq i nil)) (t (setq i (cons (car l) i)))) (setq l (cdr l))) r)) (commented-out (list-colors-display (mapcar (lambda (c) (color-value-to-name (darker (x-color-values c) 0.8))) x-colors)) (show (color-value-to-name (lighter (x-color-values "MediumSpringGreen") 0.8))) (defun letter-index (string) (mapcar (lambda (l) (- l ?@)) string )) (defun letter-incr (string inc) (concat (mapcar (lambda (l) (+ inc l ?@)) (letter-index string)))) (letter-incr "VMS" 1) (defun make-list (length init) (cond ((>= 0 length) nil) ((= 1 length) (list init)) (t (cons init (make-list (1- length) init))))) (defun make-list (length init) (let ((res nil)) (while (< 0 length) (setq res (cons init res) length (1- length))) res)) (defun letter-diff (a b) (recursive-apply 'mod (psub (string-to-list a) (string-to-list b)) (make-list (length a) 26))) ) ;;commented-out (defun dichotomy (vector value compare &optional start end key) " PRE: entry is the element to be searched in the table. (<= start end) RETURN: (values found index order) POST: (<= start index end) +-------------------+----------+-------+----------+----------------+ | Case | found | index | order | Error | +-------------------+----------+-------+----------+----------------+ | x < a[min] | FALSE | min | less | 0 | | a[i] < x < a[i+1] | FALSE | i | greater | 0 | | x = a[i] | TRUE | i | equal | 0 | | a[max] < x | FALSE | max | greater | 0 | +-------------------+----------+-------+----------+----------------+ " (setf start (or start 0) end (or end (length vector)) key (or key (function identity))) (let* ((curmin start) (curmax end) (index (truncate (+ curmin curmax) 2)) (order (funcall compare value (funcall key (aref vector index)))) ) (while (and (/= 0 order) (/= curmin index)) (if (< order 0) (setf curmax index) (setf curmin index)) (setf index (truncate (+ curmin curmax) 2)) (setf order (funcall compare value (funcall key (aref vector index))))) (when (and (< start index) (< order 0)) (setf order 1) (decf index)) (assert (or (< (funcall compare value (aref vector start)) 0) (and (< (funcall compare (aref vector index) value) 0) (or (>= (1+ index) end) (< (funcall compare value (aref vector (1+ index))) 0))) (= (funcall compare value (aref vector index)) 0))) (values (= order 0) index order))) ;;;---------------------------------------------------------------------------- ;;; utilities ;;;---------------------------------------------------------------------------- (defun ensure-list (x) (if (listp x) x (list x))) (define-modify-macro appendf (&rest args) append "Append onto list") (defun delete-from-sequence (sequence-place item &rest keywords) (apply (function delete*) item sequence-place keywords)) (define-modify-macro deletef (&rest args) delete-from-sequence "Delete from sequence") (defmacro* string-case (string-expression &body clauses) "Like case, but for strings, compared with string-equal*" (let ((value (gensym))) `(let ((,value ,string-expression)) (cond ,@(mapcar (lambda (clause) (destructuring-bind (constants &rest body) clause (if (member* constants '(t otherwise) :test (function eql)) `(t ,@body) `((member* ,value ',(ensure-list constants) :test (function cl:string-equal)) ,@body)))) clauses))))) (defun chmod (file mode) (interactive "fFile path: \nXMode: ") (set-file-modes file mode)) ;;;; THE END ;;;;