ibash: extend bash with guile call-outs.

Bash is modified to call out to Guile code whenever the user enters a
command-line; the code has the opportunity to fix up the command line in
any way.  The Guile code is under the ibash-scheme directory, all is all
newly created in this delta.

   * configure: why does this have to be in here?
   * configure.ac: look for guile and libguile.
   * eval.c: call guile_main and load ~/.bash_guile.scm.
   * shell.c: perform call-outs prior to executing command lines.
   * ibash-scheme/ibash-callout.scm: first call-out.
   * ibash-scheme/remote-sender.scm: second call-out.
   * ibash-scheme/ibash-server.scm: central intelligence service.
   * ibash-scheme/modules/i-bash: transform ib -> i-bash.
   * ibash-scheme/modules/cd: cd command line processor.
This commit is contained in:
Dale Mellor 2020-08-19 15:02:44 +01:00 committed by Dale Mellor
commit de19bb2129
9 changed files with 592 additions and 17 deletions

31
configure vendored
View file

@ -762,6 +762,7 @@ infodir
docdir docdir
oldincludedir oldincludedir
includedir includedir
runstatedir
localstatedir localstatedir
sharedstatedir sharedstatedir
sysconfdir sysconfdir
@ -891,6 +892,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc' sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com' sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var' localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include' includedir='${prefix}/include'
oldincludedir='/usr/include' oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@ -1143,6 +1145,15 @@ do
| -silent | --silent | --silen | --sile | --sil) | -silent | --silent | --silen | --sile | --sil)
silent=yes ;; silent=yes ;;
-runstatedir | --runstatedir | --runstatedi | --runstated \
| --runstate | --runstat | --runsta | --runst | --runs \
| --run | --ru | --r)
ac_prev=runstatedir ;;
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
| --run=* | --ru=* | --r=*)
runstatedir=$ac_optarg ;;
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;; ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@ -1280,7 +1291,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \ datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir libdir localedir mandir runstatedir
do do
eval ac_val=\$$ac_var eval ac_val=\$$ac_var
# Remove trailing slashes. # Remove trailing slashes.
@ -1433,6 +1444,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var] --localstatedir=DIR modifiable single-machine data [PREFIX/var]
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib] --libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include] --includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include] --oldincludedir=DIR C header files for non-gcc [/usr/include]
@ -4702,7 +4714,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807, We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */ incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1) && LARGE_OFF_T % 2147483647 == 1)
? 1 : -1]; ? 1 : -1];
@ -4748,7 +4760,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807, We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */ incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1) && LARGE_OFF_T % 2147483647 == 1)
? 1 : -1]; ? 1 : -1];
@ -4772,7 +4784,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807, We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */ incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1) && LARGE_OFF_T % 2147483647 == 1)
? 1 : -1]; ? 1 : -1];
@ -4817,7 +4829,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807, We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */ incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1) && LARGE_OFF_T % 2147483647 == 1)
? 1 : -1]; ? 1 : -1];
@ -4841,7 +4853,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807, We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */ incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1) && LARGE_OFF_T % 2147483647 == 1)
? 1 : -1]; ? 1 : -1];
@ -4921,6 +4933,9 @@ then
CFLAGS="$CFLAGS -Wno-parentheses -Wno-format-security" CFLAGS="$CFLAGS -Wno-parentheses -Wno-format-security"
fi fi
CFLAGS="${CFLAGS} $(pkg-config guile-2.2 --cflags)"
LDFLAGS="${LDFLAGS} $(pkg-config guile-2.2 --libs)"
if test "$opt_profiling" = "yes"; then if test "$opt_profiling" = "yes"; then
PROFILE_FLAGS=-pg PROFILE_FLAGS=-pg
case "$host_os" in case "$host_os" in
@ -7160,6 +7175,8 @@ main ()
if (*(data + i) != *(data3 + i)) if (*(data + i) != *(data3 + i))
return 14; return 14;
close (fd); close (fd);
free (data);
free (data3);
return 0; return 0;
} }
_ACEOF _ACEOF
@ -11001,6 +11018,8 @@ main ()
if (*(data + i) != *(data3 + i)) if (*(data + i) != *(data3 + i))
return 14; return 14;
close (fd); close (fd);
free (data);
free (data3);
return 0; return 0;
} }
_ACEOF _ACEOF

View file

@ -467,6 +467,9 @@ then
CFLAGS="$CFLAGS -Wno-parentheses -Wno-format-security" CFLAGS="$CFLAGS -Wno-parentheses -Wno-format-security"
fi fi
CFLAGS="${CFLAGS} $(pkg-config guile-2.2 --cflags)"
LDFLAGS="${LDFLAGS} $(pkg-config guile-2.2 --libs)"
dnl handle options that alter how bash is compiled and linked dnl handle options that alter how bash is compiled and linked
dnl these must come after the test for cc/gcc dnl these must come after the test for cc/gcc
if test "$opt_profiling" = "yes"; then if test "$opt_profiling" = "yes"; then

99
eval.c
View file

@ -47,6 +47,8 @@
# include "bashhist.h" # include "bashhist.h"
#endif #endif
#include <libguile.h>
extern int EOF_reached; extern int EOF_reached;
extern int indirection_level; extern int indirection_level;
extern int posixly_correct; extern int posixly_correct;
@ -64,6 +66,20 @@ extern sigset_t top_level_mask;
static void send_pwd_to_eterm __P((void)); static void send_pwd_to_eterm __P((void));
static sighandler alrm_catcher __P((int)); static sighandler alrm_catcher __P((int));
void execute_command_ (COMMAND *c)
{
/* WORD_LIST *w; */
/* for (w = c->value.Simple->words; w; w = w->next) */
/* { */
/* printf (" %s :-> %x\n", w->word->word, w->word->flags); */
/* } */
execute_command (c);
}
/* Read and execute commands until EOF is reached. This assumes that /* Read and execute commands until EOF is reached. This assumes that
the input source has already been initialized. */ the input source has already been initialized. */
int int
@ -177,7 +193,88 @@ reader_loop ()
free (ps0_string); free (ps0_string);
} }
execute_command (current_command);
if (current_command->type != cm_simple)
execute_command (current_command);
else
{
/* Need to make a SCM array of strings. */
WORD_LIST *words;
SCM a = SCM_EOL;
for (words = current_command->value.Simple->words;
words;
words = words->next)
a = scm_cons (scm_from_locale_string (words->word->word),
a);
a = scm_reverse (a);
/* !!! Need to check return from scm_c_lookup. */
SCM x = scm_call_1
(scm_variable_ref
(scm_c_lookup ("BASH:process-command-line")),
a);
if (! scm_is_true (x))
execute_command_ (current_command);
else
{
for (;;)
{
SCM r = scm_call_0
(scm_variable_ref
(scm_c_lookup ("BASH:next-command")));
if (scm_is_false (r))
break;
/* Now we have to compose a new list of words in
* current_command. */
for (words = current_command->value.Simple->words;
words;)
{
WORD_LIST *w = words->next;
/* !!! I think there is some sub-structure
* to this which also needs free'ing. */
free (words);
words = w;
}
WORD_LIST **words;
for (words = &current_command->value.Simple->words;
;
)
{
if (scm_is_null (r))
break;
*words = (WORD_LIST*) malloc (sizeof (WORD_LIST));
(*words)->next = NULL;
(*words)->word = (WORD_DESC*) malloc (sizeof (WORD_DESC));
(*words)->word->flags = 0;
(*words)->word->word = scm_to_locale_string (scm_car (r));
r = scm_cdr (r);
words = &((*words)->next);
}
execute_command_ (current_command);
}
}
}
exec_done: exec_done:
QUIT; QUIT;

View file

@ -0,0 +1,61 @@
;; THE FULL PATH TO THIS FILE SHOULD BE REFERRED TO IN THE ENVIRONMENT
;; VARIABLE "I_BASH_CALLOUT", OR ELSE COPIED TO "$HOME/.bash_guile.scm",
;; FOR EFFECT.
;; The functions in this file are called-out to whenever the user enters
;; a command on the ibash command line.
;; Copyright (C) 2020 Dale Mellor
;;
;; This file is part of iBash, the intelligent Bourne Again SHell.
;;
;; iBash 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 3 of the License, or
;; (at your option) any later version.
;;
;; iBash 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 iBash. If not, see <http://www.gnu.org/licenses/>.
(let ((f (getenv "I_BASH_CALLOUT_2")))
(cond ((and f (access? f R_OK)) (load f))
(else (let ((f (string-append (getenv "HOME")
"/i-bash/remote-sender.scm")))
(cond ((access? f R_OK) (load f))
(else
(display "iBash: ERROR: no call-out-2.\n")
(exit 1)))))))
(use-modules (db core))
(display "GUILE INITIALIZATION COMPLETE.\n")
(define command-stack '())
(define (BASH:process-command-line command-words)
(cond ((string=? (car command-words) "db")
(set! command-stack (cons (cdr command-words) command-stack))
#t)
(else
;; The process procedure comes from callout-2, loaded above.
(let ((new-stack (process command-words command-stack)))
(cond ((or (not new-stack)
(null? new-stack))
#f)
(else
(set! command-stack (reverse new-stack))
#t))))))
(define (BASH:next-command)
(cond ((null? command-stack) #f)
(else (let ((hold (car command-stack)))
(set! command-stack (cdr command-stack))
hold))))

171
ibash-scheme/ibash-server.scm Executable file
View file

@ -0,0 +1,171 @@
#!/usr/bin/guile -s ;; -*-scheme-*-
!#
(use-modules (ice-9 regex) (ice-9 popen))
;; Set up global options, reading from the command-line where possible.
(define module-dir (string-append (getenv "HOME") "/i-bash/modules"))
(define listen-port 9081)
(when (> (length (command-line)) 1) (set! module-dir (cadr (command-line))))
(when (> (length (command-line)) 2)
(set! listen-port (string->number (caddr (command-line)))))
;; Index the library of modules we have at our disposal for processing
;; command lines.
(define db-modules '())
(define verbs '())
(define word-translators '())
;; Let each module in our local file system introduce itself onto the
;; db-modules list above.
(let* ((project-dir (string-append module-dir "/"))
(dir (opendir project-dir)))
(let loop ((file (readdir dir)))
(when (not (eof-object? file))
(or (eq? #\. (string-ref file 0))
(eq? #\~ (string-ref file (1- (string-length file))))
(load (string-append project-dir "/" file)))
(loop (readdir dir)))))
;; Find the set of verbs and word translations each module provides,
;; and add to the two global lists above.
(for-each (lambda (module)
(let ((v (module-variable (resolve-module module) 'verbs)))
(when v (set! verbs (append! ((variable-ref v)) verbs))))
(let ((w (module-variable (resolve-module module)
'word-translators)))
(when w (set! word-translators (append! ((variable-ref w))
word-translators)))))
db-modules)
;; Run the word-translators over command-words, return pair of new list of
;; command words (translated), and list of used translator s-expressions.
(define (run-translators-0 command-words)
(let loop-1 ((translators word-translators)
(translator-stack '())
(words command-words))
(cond ((null? translators)
(cons words (reverse translator-stack)))
(else
(let ((translator (primitive-eval (car translators))))
(let loop-2 ((words command-words)
(new-words '())
(matched #f))
(cond ((null? words)
(loop-1 (cdr translators)
(append (if matched (list (car translators)) '())
translator-stack)
(reverse new-words)))
(else
(let ((new-word (translator (car words))))
(loop-2 (cdr words)
(cons new-word new-words)
(if (string=? new-word (car words))
matched
#t)))))))))))
;; Get the command-words translated and obtain list of used translators,
;; then concoct a new s-expression which will apply the translators again;
;; this is prepended to the incumbent command-stack and returned with the
;; translated command-words.
(define (run-translators-1 command-words command-stack)
(let* ((res (run-translators-0 command-words))
(command-words (car res))
(translator-stack (cdr res)))
(and (not (null? translator-stack))
(cons command-words
(cons `(lambda (command-words command-stack)
(let loop ((stack (quote ,translator-stack))
(command-words command-words))
(cond ((null? stack)
(cons command-words command-stack))
(else
(let ((tr (primitive-eval (car stack))))
(let loop-2 ((words command-words)
(ret '()))
(if (null? words)
(loop (cdr stack) (reverse ret))
(loop-2 (cdr words)
(cons (tr (car words))
ret)))))))))
command-stack)))))
;; We accept connections, and process intructions, from one client at a
;; time. It is expected that clients will close their connections in a
;; timely manner otherwise the system will hang for everybody using it.
(let ((socket (socket AF_INET SOCK_STREAM 0)))
(bind socket AF_INET INADDR_LOOPBACK listen-port)
(listen socket 5)
(let socket-listen-loop ()
(let ((client (car (accept socket))))
(let client-command-loop ()
(let ((argument (read client)))
(and argument
(not (eof-object? argument))
(not (null? argument))
(let* ((command-words (car argument))
(command-stack '()) ;; We don't care about this on
;; the server.
(return-stack '()) ;; --This is what we are
;; interested in building
;; here.
(run-command
(lambda (command)
(and command
(let ((ret (command command-words command-stack)))
(cond (ret
(set! command-words (car ret))
(set! command-stack (cdr ret))))
ret)))))
(run-command run-translators-1)
(set! return-stack command-stack)
(set! command-stack '())
(let loop ()
(cond ((not (null? command-words))
(and (let* ((r- (assoc-ref verbs (car command-words)))
(r (and r- (r- command-words command-stack))))
(cond ((run-command (primitive-eval r))
(set! return-stack (cons r return-stack))
#t)
(else #f)))
(loop)))))
(if (null? return-stack)
(write '() client)
(write `(lambda (command-words command-stack)
(for-each
(lambda (proc)
(and proc
(let ((ret ((primitive-eval proc)
command-words
command-stack)))
(cond (ret
(set! command-words (car ret))
(set! command-stack
(cdr ret)))))))
(quote ,(reverse return-stack)))
(cons command-words command-stack))
client))
(client-command-loop))))))
(socket-listen-loop)))

71
ibash-scheme/modules/cd Normal file
View file

@ -0,0 +1,71 @@
;; This file is loaded by ibash-server.scm, and will return a function
;; which intelligently changes a 'cd' command to go directly to a
;; directory under a specially designated set of parent directories.
;; Copyright (C) 2020 Dale Mellor
;;
;; This file is part of iBash, the intelligent Bourne Again SHell.
;;
;; iBash 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 3 of the License, or
;; (at your option) any later version.
;;
;; iBash 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 iBash. If not, see <http://www.gnu.org/licenses/>.
(set! db-modules (cons '(db server cd) db-modules))
(define-module (db server cd)
#:export (verbs))
;; Return an s-exrpression defining a function which returns a list of
;; (absolute) directory paths to search for some given directory.
(define look-list-maker--s '(lambda ()
(let ((home (getenv "HOME")))
(map (lambda (dir)
(string-append home "/" dir))
'("projects" "documents" "")))))
;; Return an s-expression which evaluates to a function which searches for
;; the requested directory and then modifies the 'cd' command-line
;; (/words/) to go directly to that directory, regardless of the current
;; position in the file system.
;;
;; The return is a cons of remaining /words/ to process, and the modified
;; 'cd' command-line prepended to the existing /stack/ of processed
;; command-lines.
(define (cd words stack)
(cond ((null? (cdr words))
'(lambda (w s) (cons '() (cons (list "cd") s))))
(else
`(lambda (words stack)
(if (access? (cadr words) R_OK)
(cons (cddr words) (cons (list "cd" (cadr words)) stack))
(let loop ((look ((primitive-eval ,look-list-maker--s))))
(cond ((null? look)
(cons (cddr words) (cons (list "cd" (cadr words))
stack)))
(else
(let ((dir (string-append (car look)
"/"
(cadr words))))
(if (access? dir R_OK)
(cons (cddr words) (cons (list "cd" dir)
stack))
(loop (cdr look))))))))))))
;; Register the above processor with lines beginning with 'cd'.
(define (verbs) (list (cons "cd" cd)))

View file

@ -0,0 +1,31 @@
;; This file is loaded by ibash-server.scm, and will replace any
;; occurrences of the token 'ib' with the token 'i-bash' on the
;; command-line.
;; Copyright (C) 2020 Dale Mellor
;;
;; This file is part of iBash, the intelligent Bourne Again SHell.
;;
;; iBash 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 3 of the License, or
;; (at your option) any later version.
;;
;; iBash 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 iBash. If not, see <http://www.gnu.org/licenses/>.
(set! db-modules (cons '(db i-bash) db-modules))
(define-module (db i-bash)
#:export (word-translators))
(define (word-translators)
'((lambda (word) (if (string=? word "ib") "i-bash" word))))

View file

@ -0,0 +1,94 @@
;; THE FULL PATH TO THIS FILE SHOULD BE REFERRED TO IN THE ENVIRONMENT
;; VARIABLE "I_BASH_CALLOUT_2", OR ELSE COPIED TO
;; "$HOME/i-bash/remote-sender.scm", FOR EFFECT.
;; The functions in this file are called-out to whenever the user enters
;; a command on the ibash command line.
;; Copyright (C) 2020 Dale Mellor
;;
;; This file is part of iBash, the intelligent Bourne Again SHell.
;;
;; iBash 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 3 of the License, or
;; (at your option) any later version.
;;
;; iBash 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 iBash. If not, see <http://www.gnu.org/licenses/>.
(define-module (db core)
#:use-module (ice-9 popen)
#:export (process))
;; Either return (cons socket action-procedure) or #f.
(define (establish-remote-call)
(let ((connection (getenv "I_BASH_REMOTE")))
(and connection
(let ((split (string-split connection #\:)))
(cond ((null? (cdr split)) #f)
(else
(catch 'getaddrinfo-error
(lambda ()
(let* ((addr (car (getaddrinfo (car split)
(cadr split) 0 AF_INET)))
(socket (socket (addrinfo:fam addr)
(addrinfo:socktype addr)
(addrinfo:protocol addr))))
(connect socket (addrinfo:addr addr))
(cons socket
(lambda (command-words command-stack)
(write (cons command-words command-stack)
socket)
(let ((return (read socket)))
(and (not (null? return))
(eval return (current-module))))))))
(lambda (key errcode) #f))))))))
;; The command-words are the tokens of the command line (list of
;; strings), and the command-stack is a list of command lines *in reverse
;; order* which are derived by the intelligence from the originals
;; (command-words).
;;
;; The return value is the updated command-stack, this being ultimately
;; sent back to the bash program and executed as if the new commands had
;; been typed at the terminal.
(define (process command-words command-stack)
(cond ((establish-remote-call)
=> (lambda (R)
(let ((server-socket (car R))
(remote-call (cdr R))
(run-command (lambda (command)
(and command
(let ((ret (command command-words
command-stack)))
(when ret
(set! command-words (car ret))
(set! command-stack (cdr ret)))
ret)))))
(let loop ()
(unless (null? command-words)
(when (run-command (remote-call command-words
command-stack))
(loop))))
(when server-socket (close server-socket)))))
(else
(set! command-words
(string-split
"echo REMOTE iBASH INTELLIGENCE SERVICE NOT AVAILABLE"
#\space))))
(if (null? command-words)
command-stack
(cons command-words command-stack)))

48
shell.c
View file

@ -89,6 +89,8 @@ extern int get_tty_state __P((void));
# include <opennt/opennt.h> # include <opennt/opennt.h>
#endif #endif
#include <libguile.h>
#if !defined (HAVE_GETPW_DECLS) #if !defined (HAVE_GETPW_DECLS)
extern struct passwd *getpwuid (); extern struct passwd *getpwuid ();
#endif /* !HAVE_GETPW_DECLS */ #endif /* !HAVE_GETPW_DECLS */
@ -353,18 +355,13 @@ _cygwin32_check_tmp ()
} }
#endif /* __CYGWIN__ */ #endif /* __CYGWIN__ */
#if defined (NO_MAIN_ENV_ARG) char **pass_env;
/* systems without third argument to main() */
int void
main (argc, argv) inner_main (closure, argc, argv)
void *closure;
int argc; int argc;
char **argv; char **argv;
#else /* !NO_MAIN_ENV_ARG */
int
main (argc, argv, env)
int argc;
char **argv, **env;
#endif /* !NO_MAIN_ENV_ARG */
{ {
register int i; register int i;
int code, old_errexit_flag; int code, old_errexit_flag;
@ -379,6 +376,8 @@ main (argc, argv, env)
env = environ; env = environ;
#endif /* __OPENNT */ #endif /* __OPENNT */
char **env = pass_env;
USE_VAR(argc); USE_VAR(argc);
USE_VAR(argv); USE_VAR(argv);
USE_VAR(env); USE_VAR(env);
@ -518,6 +517,17 @@ main (argc, argv, env)
} }
this_command_name = (char *)NULL; this_command_name = (char *)NULL;
/* Pull in the command-line processor call-out, so that the Guile
command-line processor is available in the Guile universe. */
scm_c_eval_string
("(let ((f (getenv \"I_BASH_CALLOUT\"))) "
"(cond ((and f (access? f R_OK)) (load f)) "
"(else (let ((f (string-append (getenv \"HOME\") "
"\"/.bash_guile.scm\"))) "
"(cond ((access? f R_OK) (load f)) "
"(else (display \"iBash: ERROR: no call-out.\n\") "
"(exit 1)))))))");
/* First, let the outside world know about our interactive status. /* First, let the outside world know about our interactive status.
A shell is interactive if the `-i' flag was given, or if all of A shell is interactive if the `-i' flag was given, or if all of
the following conditions are met: the following conditions are met:
@ -793,6 +803,24 @@ main (argc, argv, env)
exit_shell (last_command_exit_value); exit_shell (last_command_exit_value);
} }
#if defined (NO_MAIN_ENV_ARG)
/* systems without third argument to main() */
int
main (argc, argv)
int argc;
char **argv;
#else /* !NO_MAIN_ENV_ARG */
int
main (argc, argv, env)
int argc;
char **argv, **env;
#endif /* !NO_MAIN_ENV_ARG */
{
pass_env = env;
scm_boot_guile (argc, argv, inner_main, 0);
return 0; /* Never reached. */
}
static int static int
parse_long_options (argv, arg_start, arg_end) parse_long_options (argv, arg_start, arg_end)
char **argv; char **argv;