help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Cool and Useful LISP for the .emacs file


From: Pascal Bourguignon
Subject: Re: Cool and Useful LISP for the .emacs file
Date: 12 Nov 2003 19:21:19 +0100
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3

David Masterson <address@hidden> writes:

> >>>>> Artur Hefczyc writes:
> 
> > Kin Cho <address@hidden> writes:
> >> I used to carry around a collection of shell, sed, awk, and perl
> >> scripts to do various text/file/directory processing, as well as
> >> doing cvs/rcs stuff, running compilation and gdb etc...  Now I do
> >> (almost) all these things in elisp.
> 
> > I like this idea! I would like to use elisp as scripting language
> > also. However I would like to know if it is possible to use it that
> > way. I mean, lets assume I create elisp script to update my Linux box
> > system with new releases of some packages.
> 
> > Is it possible to run it from command line like all other scripts,
> > bash, perl etc.?
> 
> > I mean file script starting from:
> > #!/usr/bin/emacs
> 
> > Or any other elisp interpreter?
> 
> Given how big Emacs is, I would think that it would be best to start
> emacs in background (say, from your .login) and then use something
> like gnudoit or emacsclient to send your elisp to the background
> Emacs.  Done correctly, I would think that it would execute your
> scripts much faster, no?

No.

emacs IS NOT big.

$ ls -l emacs perl
-rwxr-xr-t    1 pascal   regular   4433296 2003-11-12 19:08 emacs*
-rwxr-xr-x    1 pascal   regular   1113248 2003-11-12 19:08 perl*

emacs is already in core memory so forking additionnal emacs processes
does not cost anything significant.


Starting a script  in perl or in emacs takes the  same time. (Any time
below 0.7 s IS the same time).




#!/bin/bash
echo =================================================
time emacs -batch -no-site-file -q \
    -eval '(progn (message "Hello from emacs") (kill-emacs))'
echo =================================================
time clisp -q -ansi -norc \
    -x '(progn (format t "Hello from clisp!~%") (ext:quit))'
echo =================================================
time sbcl --noinform --noprint --sysinit /dev/null --userinit /dev/null \
    --eval '(progn (format t "Hello from sbcl!~%") (SB-EXT:QUIT))'
echo =================================================
echo '(display "Hello from scsh\n") ,exit' | bash -c 'time scsh'
echo =================================================
time perl -e 'printf "Hello from perl\n";exit'
echo =================================================
exit 0



./script-times
=================================================
Hello from emacs

real  0m0.060s
user  0m0.040s
sys   0m0.010s
=================================================
Hello from clisp!

real  0m0.023s
user  0m0.010s
sys   0m0.010s   
=================================================
Hello from sbcl!

real  0m0.024s
user  0m0.010s
sys   0m0.010s   
=================================================
Scsh 0.5.3
> Hello from scsh
#f
> 
real  0m0.076s
user  0m0.030s
sys   0m0.020s   
=================================================
Hello from perl

real  0m0.009s
user  0m0.000s
sys   0m0.000s   
=================================================




To allow the use of #! for emacs, I implemented this small wrapper:


lisp-script.c:
------------------------------------------------------------------------
/******************************************************************************
FILE:               lisp-script.c
LANGUAGE:           ANSI-C
SYSTEM:             POSIX
USER-INTERFACE:     POSIX
DESCRIPTION
    This is a simple tool that encapsulate clisp or emacs invokation as
    script interpreters.
USAGE
    Insert #!/usr/local/bin/emacs-script
    or     #!/usr/local/bin/clisp-script
    as the first line of a lisp program, and run it.
AUTHORS
    <PJB> Pascal J. Bourguignon
MODIFICATIONS
    2003-10-27 <PJB> Added SBCL.
    2002-04-06 <PJB> Creation.
BUGS
LEGAL
    GPL
    Copyright Pascal J. Bourguignon 2002 - 2003

    This file is part of lisp script tools.

    This  program is  free software;  you can  redistribute  it and/or
    modify it  under the  terms of the  GNU General Public  License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.

    This program  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
    General Public License for more details.

    You should have received a  copy of the GNU General Public License
    along with  this program; see the  file COPYING; if  not, write to
    the Free  Software Foundation, Inc.,  59 Temple Place,  Suite 330,
    Boston, MA 02111-1307 USA
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sysexits.h>

#ifndef EMACS
#define EMACS "/usr/local/bin/emacs"
#endif

#ifndef CLISP
#define CLISP "/usr/local/bin/clisp"
#endif

#ifndef SBCL
#define SBCL  "/usr/local/bin/sbcl"
#endif


#ifdef MACOSX

    /* Not exactly correct. We should grab GNU dirname/basename.*/

    static const char* basename(const char* path)
    {
        const char* slash=strrchr(path,'/');
        if(slash==0){
            return(path);
        }else{
            return(slash+1);
        }
    }/*basename*/

#else
#include <libgen.h>
#endif


    const char* pname="lisp-script";

    void usage(const char* pname)
    {
        fprintf(stderr,
                "%s usage:\n"\
                "    %s -h|--help | script-file [script-arguments...]\n",
                pname,pname);
    }/*usage*/

                
int main(int argc,char** argv)
{
    int i;
    int length;
    int position;
    char* script_file=0;
    char* script_option;
    char* init_file;
    enum { emacs, clisp, sbcl } kind;

    pname=basename(argv[0]);
    if(strcmp(pname,"emacs-script")==0){
        kind=emacs;
    }else if(strcmp(pname,"clisp-script")==0){
        kind=clisp;
    }else if(strcmp(pname,"sbcl-script")==0){
        kind=sbcl;
    }else{
      lisp_script:
        fprintf(stderr,"%s: should be invoked either as emacs-script, "
                "clisp-script or sbcl-script.\n",pname);
        return(EX_USAGE);        
    }

    if((strcmp(argv[1],"-h")==0)||(strcmp(argv[1],"--help")==0)){
        usage(pname);
        return(EX_OK);
    }

    if(1<argc){
        script_file=argv[1];
    }else{
        fprintf(stderr,"%s: missing script-file argument.\n",pname);
        usage(pname);
        return(EX_USAGE);        
    }

    if(0!=access(script_file,R_OK)){
        fprintf(stderr,"Can't read script file '%s'.\n",script_file);
        return(EX_NOINPUT);
    }

    init_file=(char*)malloc(strlen(argv[0])+8);
    switch(kind){
    case emacs:
        sprintf(init_file,"%s.el",argv[0]);
        break;
    case clisp:
    case sbcl:
        sprintf(init_file,"%s.lisp",argv[0]);
        break;
    default:
        goto lisp_script;
    }

    if(0!=access(init_file,R_OK)){
        fprintf(stderr,"Can't read %s init file '%s'.\n",argv[0],init_file);
        return(EX_UNAVAILABLE);
    }

    length=0;
    for(i=1;i<argc;i++){
        length+=3+strlen(argv[i]);
    }
    script_option=(char*)malloc(length+16);
    position=0;
    position+=sprintf(script_option+position,"(script ");
    for(i=1;i<argc;i++){
        position+=sprintf(script_option+position,"\"%s\" ",argv[i]);
    }
    position+=sprintf(script_option+position,")");
    /*
    fprintf(stderr,"init_file=%s\n",init_file);
    fprintf(stderr,"script_option=%s\n",script_option);
    */

    switch(kind){
    case emacs:
        execl(EMACS,"emacs","--batch","--no-site-file","--no-init-file",
              "--load",init_file,
              "--eval",script_option,
              0);
        fprintf(stderr,"Can't execl '%s'.\n",EMACS);
        break;
    case clisp:
        execl(CLISP,"clisp","-q",
              "-i",init_file,
              "-x",script_option,
              0);
        fprintf(stderr,"Can't execl '%s'.\n",CLISP);
        break;
    case sbcl:
        execl(SBCL,"sbcl","--noinform",
              "--userinit",init_file,
              "--eval",script_option,
              0);
        fprintf(stderr,"Can't execl '%s'.\n",SBCL);
        break;
    default:
        goto lisp_script;
    }
    return(EX_UNAVAILABLE);
}/*main*/

/*** lisp-script.c                    -- 2003-10-27 22:57:16 -- pascal   ***/
------------------------------------------------------------------------


emacs-script.el:
------------------------------------------------------------------------
;;
;; elisp initialization for emacs scripts.
;; We try to install some stuff to make it more like Common-Lisp.
;; GPL

;; (add-to-list 'load-path "")

(require 'cl)


(defun script (file &rest arguments)
  "
DO:     reads the FILE, skip the first line if it begins with '#!' and   
"
  (unless (file-readable-p file)
    (error "Can't read file %S." file))
  (setq ext:*args* arguments)
  (setq *load-pathname* file)
  (setq *LOAD-PATHNAME* *load-pathname*
        EXT:*ARGS*      ext:*args*)
  (find-file file)
  (let ((buf (current-buffer)))
    (unwind-protect
        (progn
          (toggle-read-only 1)
          (goto-char (point-min))
          (if (looking-at "#!")
              (forward-line 1))
          (narrow-to-region (point) (point-max))
          (eval-buffer)
          )
      (kill-buffer buf)))
  );;script




;; Exit status from <sysexit.h>
(defconst  EX_OK           0       "successful termination")
(defconst  EX__BASE        64      "base value for error messages")
(defconst  EX_USAGE        64      "command line usage error")
(defconst  EX_DATAERR      65      "data format error")
(defconst  EX_NOINPUT      66      "cannot open input")
(defconst  EX_NOUSER       67      "addressee unknown")
(defconst  EX_NOHOST       68      "host name unknown")
(defconst  EX_UNAVAILABLE  69      "service unavailable")
(defconst  EX_SOFTWARE     70      "internal software error")
(defconst  EX_OSERR        71      "system error (e.g., can't fork)")
(defconst  EX_OSFILE       72      "critical OS file missing")
(defconst  EX_CANTCREAT    73      "can't create (user) output file")
(defconst  EX_IOERR        74      "input/output error")
(defconst  EX_TEMPFAIL     75      "temp failure; user is invited to retry")
(defconst  EX_PROTOCOL     76      "remote error in protocol")
(defconst  EX_NOPERM       77      "permission denied")
(defconst  EX_CONFIG       78      "configuration error")
(defconst  EX__MAX         78      "maximum listed value")


(defun ext:exit (&optional status)
  "
DO:     stops the script, exiting with the given status.
"
  (unless status (setq status EX_OK))
  (kill-emacs status)
  );;ext:exit


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Common-Lisp / Elisp 

(defun read-line (&optional input-stream eof-error-p eof-value recursive-p)
  "
DO:     Implement partially Common-Lisp read-line function.
"
  (unless input-stream (setq input-stream standard-input))
  (let ( (standard-input input-stream)
         (line nil)
         (char (read-char)) )
    (while (not (member char '(10 13)))
      (push char line)
      (setq char (read-char)) )
    (funcall 'concat  (nreverse line))
    );;let
  );;read-line



(defalias 'EXT:EXIT  'ext:exit)
(defalias 'READ-LINE 'read-line)

;;;; emacs-script.el                  -- 2003-02-08 04:35:08 -- pascal   ;;;;
------------------------------------------------------------------------





-- 
__Pascal_Bourguignon__
http://www.informatimago.com/


reply via email to

[Prev in Thread] Current Thread [Next in Thread]