diff --git a/ffi-private.h b/ffi-private.h
index 5467dac..849d1b7 100644
--- a/ffi-private.h
+++ b/ffi-private.h
@@ -1,132 +1,134 @@
/* FFI interface for TinySCHEME.
*
* Copyright (C) 2016 g10 code GmbH
*
* This file is part of GnuPG.
*
* GnuPG 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.
*
* GnuPG 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; if not, see .
*/
#ifndef GPGSCM_FFI_PRIVATE_H
#define GPGSCM_FFI_PRIVATE_H
#include
#include "scheme.h"
#include "scheme-private.h"
#define FFI_PROLOG() \
unsigned int ffi_arg_index GPGRT_ATTR_UNUSED = 1; \
int err GPGRT_ATTR_UNUSED = 0 \
int ffi_bool_value (scheme *sc, pointer p);
#define CONVERSION_number(SC, X) (SC)->vptr->ivalue (X)
#define CONVERSION_string(SC, X) (SC)->vptr->string_value (X)
+#define CONVERSION_character(SC, X) (SC)->vptr->charvalue (X)
#define CONVERSION_list(SC, X) (X)
#define CONVERSION_bool(SC, X) ffi_bool_value ((SC), (X))
#define CONVERSION_path(SC, X) (((SC)->vptr->is_string (X) \
? (SC)->vptr->string_value \
: (SC)->vptr->symname) (X))
#define IS_A_number(SC, X) (SC)->vptr->is_number (X)
#define IS_A_string(SC, X) (SC)->vptr->is_string (X)
+#define IS_A_character(SC, X) (SC)->vptr->is_character (X)
#define IS_A_list(SC, X) (SC)->vptr->is_list ((SC), X)
#define IS_A_bool(SC, X) ((X) == (SC)->F || (X) == (SC)->T)
#define IS_A_path(SC, X) ((SC)->vptr->is_string (X) \
|| (SC)->vptr->is_symbol (X))
#define FFI_ARG_OR_RETURN(SC, CTYPE, TARGET, WANT, ARGS) \
do { \
if ((ARGS) == (SC)->NIL) \
return (SC)->vptr->mk_string ((SC), \
"too few arguments: want " \
#TARGET "("#WANT"/"#CTYPE")\n"); \
if (! IS_A_##WANT ((SC), pair_car (ARGS))) { \
char ffi_error_message[256]; \
snprintf (ffi_error_message, sizeof ffi_error_message, \
"argument %d must be: " #WANT "\n", ffi_arg_index); \
return (SC)->vptr->mk_string ((SC), ffi_error_message); \
} \
TARGET = CONVERSION_##WANT (SC, pair_car (ARGS)); \
ARGS = pair_cdr (ARGS); \
ffi_arg_index += 1; \
} while (0)
#define FFI_ARGS_DONE_OR_RETURN(SC, ARGS) \
do { \
if ((ARGS) != (SC)->NIL) \
return (SC)->vptr->mk_string ((SC), "too many arguments"); \
} while (0)
#define FFI_RETURN_ERR(SC, ERR) \
return _cons ((SC), mk_integer ((SC), (ERR)), (SC)->NIL, 1)
#define FFI_RETURN(SC) FFI_RETURN_ERR (SC, err)
#define FFI_RETURN_POINTER(SC, X) \
return _cons ((SC), mk_integer ((SC), err), \
_cons ((SC), (X), (SC)->NIL, 1), 1)
#define FFI_RETURN_INT(SC, X) \
FFI_RETURN_POINTER ((SC), mk_integer ((SC), (X)))
#define FFI_RETURN_STRING(SC, X) \
FFI_RETURN_POINTER ((SC), mk_string ((SC), (X)))
const char *ffi_schemify_name (const char *s, int macro);
void ffi_scheme_eval (scheme *sc, const char *format, ...)
GPGRT_ATTR_PRINTF (2, 3);
pointer ffi_sprintf (scheme *sc, const char *format, ...)
GPGRT_ATTR_PRINTF (2, 3);
#define ffi_define_function_name(SC, NAME, F) \
do { \
scheme_define ((SC), \
(SC)->global_env, \
mk_symbol ((SC), ffi_schemify_name ("_" #F, 0)), \
mk_foreign_func ((SC), (do_##F))); \
ffi_scheme_eval ((SC), \
"(define (%s . a) (ffi-apply \"%s\" %s a))", \
(NAME), (NAME), ffi_schemify_name ("_" #F, 0)); \
} while (0)
#define ffi_define_function(SC, F) \
ffi_define_function_name ((SC), ffi_schemify_name (#F, 0), F)
#define ffi_define_constant(SC, C) \
scheme_define ((SC), \
(SC)->global_env, \
mk_symbol ((SC), ffi_schemify_name (#C, 1)), \
mk_integer ((SC), (C)))
#define ffi_define(SC, SYM, EXP) \
scheme_define ((SC), (SC)->global_env, mk_symbol ((SC), (SYM)), EXP)
#define ffi_define_variable_pointer(SC, C, P) \
scheme_define ((SC), \
(SC)->global_env, \
mk_symbol ((SC), ffi_schemify_name (#C, 0)), \
(P))
#define ffi_define_variable_integer(SC, C) \
ffi_define_variable_pointer ((SC), C, (SC)->vptr->mk_integer ((SC), C))
#define ffi_define_variable_string(SC, C) \
ffi_define_variable_pointer ((SC), C, (SC)->vptr->mk_string ((SC), C ?: ""))
gpg_error_t ffi_list2argv (scheme *sc, pointer list,
char ***argv, size_t *len);
gpg_error_t ffi_list2intv (scheme *sc, pointer list,
int **intv, size_t *len);
#endif /* GPGSCM_FFI_PRIVATE_H */
diff --git a/ffi.c b/ffi.c
index babf1e1..fe418fc 100644
--- a/ffi.c
+++ b/ffi.c
@@ -1,1167 +1,1236 @@
/* FFI interface for TinySCHEME.
*
* Copyright (C) 2016 g10 code GmbH
*
* This file is part of GnuPG.
*
* GnuPG 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.
*
* GnuPG 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; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if HAVE_LIBREADLINE
#include
#include
#endif
#include "../../common/util.h"
#include "../../common/exechelp.h"
#include "../../common/sysutils.h"
#include "private.h"
#include "ffi.h"
#include "ffi-private.h"
int
ffi_bool_value (scheme *sc, pointer p)
{
return ! (p == sc->F);
}
static pointer
do_logand (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int v, acc = ~0;
while (args != sc->NIL)
{
FFI_ARG_OR_RETURN (sc, unsigned int, v, number, args);
acc &= v;
}
FFI_RETURN_INT (sc, acc);
}
static pointer
do_logior (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int v, acc = 0;
while (args != sc->NIL)
{
FFI_ARG_OR_RETURN (sc, unsigned int, v, number, args);
acc |= v;
}
FFI_RETURN_INT (sc, acc);
}
static pointer
do_logxor (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int v, acc = 0;
while (args != sc->NIL)
{
FFI_ARG_OR_RETURN (sc, unsigned int, v, number, args);
acc ^= v;
}
FFI_RETURN_INT (sc, acc);
}
static pointer
do_lognot (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int v;
FFI_ARG_OR_RETURN (sc, unsigned int, v, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_INT (sc, ~v);
}
/* User interface. */
static pointer
do_flush_stdio (scheme *sc, pointer args)
{
FFI_PROLOG ();
FFI_ARGS_DONE_OR_RETURN (sc, args);
fflush (stdout);
fflush (stderr);
FFI_RETURN (sc);
}
int use_libreadline;
/* Read a string, and return a pointer to it. Returns NULL on EOF. */
char *
rl_gets (const char *prompt)
{
static char *line = NULL;
char *p;
xfree (line);
#if HAVE_LIBREADLINE
{
line = readline (prompt);
if (line && *line)
add_history (line);
}
#else
{
size_t max_size = 0xff;
printf ("%s", prompt);
fflush (stdout);
line = xtrymalloc (max_size);
if (line != NULL)
fgets (line, max_size, stdin);
}
#endif
/* Strip trailing whitespace. */
if (line && strlen (line) > 0)
for (p = &line[strlen (line) - 1]; isspace (*p); p--)
*p = 0;
return line;
}
static pointer
do_prompt (scheme *sc, pointer args)
{
FFI_PROLOG ();
const char *prompt;
const char *line;
FFI_ARG_OR_RETURN (sc, const char *, prompt, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
line = rl_gets (prompt);
if (! line)
FFI_RETURN_POINTER (sc, sc->EOF_OBJ);
FFI_RETURN_STRING (sc, line);
}
static pointer
do_sleep (scheme *sc, pointer args)
{
FFI_PROLOG ();
unsigned int seconds;
FFI_ARG_OR_RETURN (sc, unsigned int, seconds, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
sleep (seconds);
FFI_RETURN (sc);
}
static pointer
do_usleep (scheme *sc, pointer args)
{
FFI_PROLOG ();
useconds_t microseconds;
FFI_ARG_OR_RETURN (sc, useconds_t, microseconds, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
usleep (microseconds);
FFI_RETURN (sc);
}
static pointer
do_chdir (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
FFI_ARG_OR_RETURN (sc, char *, name, path, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (chdir (name))
FFI_RETURN_ERR (sc, errno);
FFI_RETURN (sc);
}
static pointer
do_strerror (scheme *sc, pointer args)
{
FFI_PROLOG ();
int error;
FFI_ARG_OR_RETURN (sc, int, error, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_STRING (sc, gpg_strerror (error));
}
static pointer
do_getenv (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_STRING (sc, getenv (name) ?: "");
}
static pointer
do_setenv (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
char *value;
int overwrite;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARG_OR_RETURN (sc, char *, value, string, args);
FFI_ARG_OR_RETURN (sc, int, overwrite, bool, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_ERR (sc, gnupg_setenv (name, value, overwrite));
}
static pointer
do_exit (scheme *sc, pointer args)
{
FFI_PROLOG ();
int retcode;
FFI_ARG_OR_RETURN (sc, int, retcode, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
exit (retcode);
}
/* XXX: use gnupgs variant b/c mode as string */
static pointer
do_open (scheme *sc, pointer args)
{
FFI_PROLOG ();
int fd;
char *pathname;
int flags;
mode_t mode = 0;
FFI_ARG_OR_RETURN (sc, char *, pathname, path, args);
FFI_ARG_OR_RETURN (sc, int, flags, number, args);
if (args != sc->NIL)
FFI_ARG_OR_RETURN (sc, mode_t, mode, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
fd = open (pathname, flags, mode);
if (fd == -1)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN_INT (sc, fd);
}
static pointer
do_fdopen (scheme *sc, pointer args)
{
FFI_PROLOG ();
FILE *stream;
int fd;
char *mode;
int kind;
FFI_ARG_OR_RETURN (sc, int, fd, number, args);
FFI_ARG_OR_RETURN (sc, char *, mode, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
stream = fdopen (fd, mode);
if (stream == NULL)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
if (setvbuf (stream, NULL, _IONBF, 0) != 0)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
kind = 0;
if (strchr (mode, 'r'))
kind |= port_input;
if (strchr (mode, 'w'))
kind |= port_output;
FFI_RETURN_POINTER (sc, sc->vptr->mk_port_from_file (sc, stream, kind));
}
static pointer
do_close (scheme *sc, pointer args)
{
FFI_PROLOG ();
int fd;
FFI_ARG_OR_RETURN (sc, int, fd, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_ERR (sc, close (fd) == 0 ? 0 : gpg_error_from_syserror ());
}
static pointer
do_mkdtemp (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *template;
char buffer[128];
FFI_ARG_OR_RETURN (sc, char *, template, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (strlen (template) > sizeof buffer - 1)
FFI_RETURN_ERR (sc, EINVAL);
strncpy (buffer, template, sizeof buffer);
FFI_RETURN_STRING (sc, gnupg_mkdtemp (buffer));
}
static pointer
do_unlink (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (unlink (name) == -1)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN (sc);
}
static gpg_error_t
unlink_recursively (const char *name)
{
gpg_error_t err = 0;
struct stat st;
if (stat (name, &st) == -1)
return gpg_error_from_syserror ();
if (S_ISDIR (st.st_mode))
{
DIR *dir;
struct dirent *dent;
dir = opendir (name);
if (dir == NULL)
return gpg_error_from_syserror ();
while ((dent = readdir (dir)))
{
char *child;
if (strcmp (dent->d_name, ".") == 0
|| strcmp (dent->d_name, "..") == 0)
continue;
child = xtryasprintf ("%s/%s", name, dent->d_name);
if (child == NULL)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = unlink_recursively (child);
xfree (child);
if (err == gpg_error_from_errno (ENOENT))
err = 0;
if (err)
goto leave;
}
leave:
closedir (dir);
if (! err)
rmdir (name);
return err;
}
else
if (unlink (name) == -1)
return gpg_error_from_syserror ();
return 0;
}
static pointer
do_unlink_recursively (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = unlink_recursively (name);
FFI_RETURN (sc);
}
static pointer
do_rename (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *old;
char *new;
FFI_ARG_OR_RETURN (sc, char *, old, string, args);
FFI_ARG_OR_RETURN (sc, char *, new, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (rename (old, new) == -1)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN (sc);
}
static pointer
do_getcwd (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer result;
char *cwd;
FFI_ARGS_DONE_OR_RETURN (sc, args);
cwd = gnupg_getcwd ();
if (cwd == NULL)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
result = sc->vptr->mk_string (sc, cwd);
xfree (cwd);
FFI_RETURN_POINTER (sc, result);
}
static pointer
do_mkdir (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
char *mode;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARG_OR_RETURN (sc, char *, mode, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (gnupg_mkdir (name, mode) == -1)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN (sc);
}
static pointer
do_rmdir (scheme *sc, pointer args)
{
FFI_PROLOG ();
char *name;
FFI_ARG_OR_RETURN (sc, char *, name, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (rmdir (name) == -1)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
FFI_RETURN (sc);
}
/* estream functions. */
struct es_object_box
{
estream_t stream;
int closed;
};
static void
es_object_finalize (scheme *sc, void *data)
{
struct es_object_box *box = data;
(void) sc;
if (! box->closed)
es_fclose (box->stream);
xfree (box);
}
static void
es_object_to_string (scheme *sc, char *out, size_t size, void *data)
{
struct es_object_box *box = data;
(void) sc;
snprintf (out, size, "#estream %p", box->stream);
}
static struct foreign_object_vtable es_object_vtable =
{
es_object_finalize,
es_object_to_string,
};
static pointer
es_wrap (scheme *sc, estream_t stream)
{
struct es_object_box *box = xmalloc (sizeof *box);
if (box == NULL)
return sc->NIL;
box->stream = stream;
box->closed = 0;
return sc->vptr->mk_foreign_object (sc, &es_object_vtable, box);
}
static struct es_object_box *
es_unwrap (scheme *sc, pointer object)
{
(void) sc;
if (! is_foreign_object (object))
return NULL;
if (sc->vptr->get_foreign_object_vtable (object) != &es_object_vtable)
return NULL;
return sc->vptr->get_foreign_object_data (object);
}
#define CONVERSION_estream(SC, X) es_unwrap (SC, X)
#define IS_A_estream(SC, X) es_unwrap (SC, X)
static pointer
do_es_fclose (scheme *sc, pointer args)
{
FFI_PROLOG ();
struct es_object_box *box;
FFI_ARG_OR_RETURN (sc, struct es_object_box *, box, estream, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = es_fclose (box->stream);
if (! err)
box->closed = 1;
FFI_RETURN (sc);
}
static pointer
do_es_read (scheme *sc, pointer args)
{
FFI_PROLOG ();
struct es_object_box *box;
size_t bytes_to_read;
pointer result;
void *buffer;
size_t bytes_read;
FFI_ARG_OR_RETURN (sc, struct es_object_box *, box, estream, args);
FFI_ARG_OR_RETURN (sc, size_t, bytes_to_read, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
buffer = xtrymalloc (bytes_to_read);
if (buffer == NULL)
FFI_RETURN_ERR (sc, ENOMEM);
err = es_read (box->stream, buffer, bytes_to_read, &bytes_read);
if (err)
FFI_RETURN_ERR (sc, err);
result = sc->vptr->mk_counted_string (sc, buffer, bytes_read);
xfree (buffer);
FFI_RETURN_POINTER (sc, result);
}
static pointer
do_es_feof (scheme *sc, pointer args)
{
FFI_PROLOG ();
struct es_object_box *box;
FFI_ARG_OR_RETURN (sc, struct es_object_box *, box, estream, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
FFI_RETURN_POINTER (sc, es_feof (box->stream) ? sc->T : sc->F);
}
static pointer
do_es_write (scheme *sc, pointer args)
{
FFI_PROLOG ();
struct es_object_box *box;
const char *buffer;
size_t bytes_to_write, bytes_written;
FFI_ARG_OR_RETURN (sc, struct es_object_box *, box, estream, args);
/* XXX how to get the length of the string buffer? scheme strings
may contain \0. */
FFI_ARG_OR_RETURN (sc, const char *, buffer, string, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
bytes_to_write = strlen (buffer);
while (bytes_to_write > 0)
{
err = es_write (box->stream, buffer, bytes_to_write, &bytes_written);
if (err)
break;
bytes_to_write -= bytes_written;
buffer += bytes_written;
}
FFI_RETURN (sc);
}
/* Process handling. */
static pointer
do_spawn_process (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer arguments;
char **argv;
size_t len;
unsigned int flags;
estream_t infp;
estream_t outfp;
estream_t errfp;
pid_t pid;
FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args);
FFI_ARG_OR_RETURN (sc, unsigned int, flags, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = ffi_list2argv (sc, arguments, &argv, &len);
if (err == gpg_error (GPG_ERR_INV_VALUE))
return ffi_sprintf (sc, "%luth element of first argument is "
"neither string nor symbol",
(unsigned long) len);
if (err)
FFI_RETURN_ERR (sc, err);
if (verbose > 1)
{
char **p;
fprintf (stderr, "Executing:");
for (p = argv; *p; p++)
fprintf (stderr, " '%s'", *p);
fprintf (stderr, "\n");
}
err = gnupg_spawn_process (argv[0], (const char **) &argv[1],
GPG_ERR_SOURCE_DEFAULT,
NULL,
flags,
&infp, &outfp, &errfp, &pid);
xfree (argv);
#define IMC(A, B) \
_cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1)
#define IMS(A, B) \
_cons (sc, es_wrap (sc, (A)), (B), 1)
FFI_RETURN_POINTER (sc, IMS (infp,
IMS (outfp,
IMS (errfp,
IMC (pid, sc->NIL)))));
#undef IMS
#undef IMC
}
static pointer
do_spawn_process_fd (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer arguments;
char **argv;
size_t len;
int infd, outfd, errfd;
pid_t pid;
FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args);
FFI_ARG_OR_RETURN (sc, int, infd, number, args);
FFI_ARG_OR_RETURN (sc, int, outfd, number, args);
FFI_ARG_OR_RETURN (sc, int, errfd, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = ffi_list2argv (sc, arguments, &argv, &len);
if (err == gpg_error (GPG_ERR_INV_VALUE))
return ffi_sprintf (sc, "%luth element of first argument is "
"neither string nor symbol",
(unsigned long) len);
if (err)
FFI_RETURN_ERR (sc, err);
if (verbose > 1)
{
char **p;
fprintf (stderr, "Executing:");
for (p = argv; *p; p++)
fprintf (stderr, " '%s'", *p);
fprintf (stderr, "\n");
}
err = gnupg_spawn_process_fd (argv[0], (const char **) &argv[1],
infd, outfd, errfd, &pid);
xfree (argv);
FFI_RETURN_INT (sc, pid);
}
static pointer
do_wait_process (scheme *sc, pointer args)
{
FFI_PROLOG ();
const char *name;
pid_t pid;
int hang;
int retcode;
FFI_ARG_OR_RETURN (sc, const char *, name, string, args);
FFI_ARG_OR_RETURN (sc, pid_t, pid, number, args);
FFI_ARG_OR_RETURN (sc, int, hang, bool, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = gnupg_wait_process (name, pid, hang, &retcode);
if (err == GPG_ERR_GENERAL)
err = 0; /* Let the return code speak for itself. */
FFI_RETURN_INT (sc, retcode);
}
static pointer
do_wait_processes (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer list_names;
char **names;
pointer list_pids;
size_t i, count;
pid_t *pids;
int hang;
int *retcodes;
pointer retcodes_list = sc->NIL;
FFI_ARG_OR_RETURN (sc, pointer, list_names, list, args);
FFI_ARG_OR_RETURN (sc, pointer, list_pids, list, args);
FFI_ARG_OR_RETURN (sc, int, hang, bool, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
if (sc->vptr->list_length (sc, list_names)
!= sc->vptr->list_length (sc, list_pids))
return
sc->vptr->mk_string (sc, "length of first two arguments must match");
err = ffi_list2argv (sc, list_names, &names, &count);
if (err == gpg_error (GPG_ERR_INV_VALUE))
return ffi_sprintf (sc, "%luth element of first argument is "
"neither string nor symbol",
(unsigned long) count);
if (err)
FFI_RETURN_ERR (sc, err);
err = ffi_list2intv (sc, list_pids, (int **) &pids, &count);
if (err == gpg_error (GPG_ERR_INV_VALUE))
return ffi_sprintf (sc, "%luth element of second argument is "
"neither string nor symbol",
(unsigned long) count);
if (err)
FFI_RETURN_ERR (sc, err);
retcodes = xtrycalloc (sizeof *retcodes, count);
if (retcodes == NULL)
{
xfree (names);
xfree (pids);
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
}
err = gnupg_wait_processes ((const char **) names, pids, count, hang,
retcodes);
if (err == GPG_ERR_GENERAL)
err = 0; /* Let the return codes speak. */
for (i = 0; i < count; i++)
retcodes_list =
(sc->vptr->cons) (sc,
sc->vptr->mk_integer (sc,
(long) retcodes[count-1-i]),
retcodes_list);
FFI_RETURN_POINTER (sc, retcodes_list);
}
static pointer
do_pipe (scheme *sc, pointer args)
{
FFI_PROLOG ();
int filedes[2];
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = gnupg_create_pipe (filedes);
#define IMC(A, B) \
_cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1)
FFI_RETURN_POINTER (sc, IMC (filedes[0],
IMC (filedes[1], sc->NIL)));
#undef IMC
}
static pointer
do_inbound_pipe (scheme *sc, pointer args)
{
FFI_PROLOG ();
int filedes[2];
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = gnupg_create_inbound_pipe (filedes, NULL, 0);
#define IMC(A, B) \
_cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1)
FFI_RETURN_POINTER (sc, IMC (filedes[0],
IMC (filedes[1], sc->NIL)));
#undef IMC
}
static pointer
do_outbound_pipe (scheme *sc, pointer args)
{
FFI_PROLOG ();
int filedes[2];
FFI_ARGS_DONE_OR_RETURN (sc, args);
err = gnupg_create_outbound_pipe (filedes, NULL, 0);
#define IMC(A, B) \
_cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1)
FFI_RETURN_POINTER (sc, IMC (filedes[0],
IMC (filedes[1], sc->NIL)));
#undef IMC
}
/* Test helper functions. */
static pointer
do_file_equal (scheme *sc, pointer args)
{
FFI_PROLOG ();
pointer result = sc->F;
char *a_name, *b_name;
int binary;
const char *mode;
FILE *a_stream = NULL, *b_stream = NULL;
struct stat a_stat, b_stat;
#define BUFFER_SIZE 1024
char a_buf[BUFFER_SIZE], b_buf[BUFFER_SIZE];
#undef BUFFER_SIZE
size_t chunk;
FFI_ARG_OR_RETURN (sc, char *, a_name, string, args);
FFI_ARG_OR_RETURN (sc, char *, b_name, string, args);
FFI_ARG_OR_RETURN (sc, int, binary, bool, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
mode = binary ? "rb" : "r";
a_stream = fopen (a_name, mode);
if (a_stream == NULL)
goto errout;
b_stream = fopen (b_name, mode);
if (b_stream == NULL)
goto errout;
if (fstat (fileno (a_stream), &a_stat) < 0)
goto errout;
if (fstat (fileno (b_stream), &b_stat) < 0)
goto errout;
if (binary && a_stat.st_size != b_stat.st_size)
{
if (verbose)
fprintf (stderr, "Files %s and %s differ in size %lu != %lu\n",
a_name, b_name, (unsigned long) a_stat.st_size,
(unsigned long) b_stat.st_size);
goto out;
}
while (! feof (a_stream))
{
chunk = sizeof a_buf;
chunk = fread (a_buf, 1, chunk, a_stream);
if (chunk == 0 && ferror (a_stream))
goto errout; /* some error */
if (fread (b_buf, 1, chunk, b_stream) < chunk)
{
if (feof (b_stream))
goto out; /* short read */
goto errout; /* some error */
}
if (chunk > 0 && memcmp (a_buf, b_buf, chunk) != 0)
goto out;
}
fread (b_buf, 1, 1, b_stream);
if (! feof (b_stream))
goto out; /* b is longer */
/* They match. */
result = sc->T;
out:
if (a_stream)
fclose (a_stream);
if (b_stream)
fclose (b_stream);
FFI_RETURN_POINTER (sc, result);
errout:
err = gpg_error_from_syserror ();
goto out;
}
static pointer
do_splice (scheme *sc, pointer args)
{
FFI_PROLOG ();
int source;
int sink;
ssize_t len = -1;
char buffer[1024];
ssize_t bytes_read;
FFI_ARG_OR_RETURN (sc, int, source, number, args);
FFI_ARG_OR_RETURN (sc, int, sink, number, args);
if (args != sc->NIL)
FFI_ARG_OR_RETURN (sc, ssize_t, len, number, args);
FFI_ARGS_DONE_OR_RETURN (sc, args);
while (len == -1 || len > 0)
{
size_t want = sizeof buffer;
if (len > 0 && (ssize_t) want > len)
want = (size_t) len;
bytes_read = read (source, buffer, want);
if (bytes_read == 0)
break;
if (bytes_read < 0)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
if (write (sink, buffer, bytes_read) != bytes_read)
FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
if (len != -1)
len -= bytes_read;
}
FFI_RETURN (sc);
}
+static pointer
+do_string_index (scheme *sc, pointer args)
+{
+ FFI_PROLOG ();
+ char *haystack;
+ char needle;
+ ssize_t offset = 0;
+ char *position;
+ FFI_ARG_OR_RETURN (sc, char *, haystack, string, args);
+ FFI_ARG_OR_RETURN (sc, char, needle, character, args);
+ if (args != sc->NIL)
+ {
+ FFI_ARG_OR_RETURN (sc, ssize_t, offset, number, args);
+ if (offset < 0)
+ return ffi_sprintf (sc, "offset must be positive");
+ if (offset > strlen (haystack))
+ return ffi_sprintf (sc, "offset exceeds haystack");
+ }
+ FFI_ARGS_DONE_OR_RETURN (sc, args);
+
+ position = strchr (haystack+offset, needle);
+ if (position)
+ FFI_RETURN_INT (sc, position - haystack);
+ else
+ FFI_RETURN_POINTER (sc, sc->F);
+}
+
+static pointer
+do_string_rindex (scheme *sc, pointer args)
+{
+ FFI_PROLOG ();
+ char *haystack;
+ char needle;
+ ssize_t offset = 0;
+ char *position;
+ FFI_ARG_OR_RETURN (sc, char *, haystack, string, args);
+ FFI_ARG_OR_RETURN (sc, char, needle, character, args);
+ if (args != sc->NIL)
+ {
+ FFI_ARG_OR_RETURN (sc, ssize_t, offset, number, args);
+ if (offset < 0)
+ return ffi_sprintf (sc, "offset must be positive");
+ if (offset > strlen (haystack))
+ return ffi_sprintf (sc, "offset exceeds haystack");
+ }
+ FFI_ARGS_DONE_OR_RETURN (sc, args);
+
+ position = strrchr (haystack+offset, needle);
+ if (position)
+ FFI_RETURN_INT (sc, position - haystack);
+ else
+ FFI_RETURN_POINTER (sc, sc->F);
+}
+
+static pointer
+do_string_contains (scheme *sc, pointer args)
+{
+ FFI_PROLOG ();
+ char *haystack;
+ char *needle;
+ FFI_ARG_OR_RETURN (sc, char *, haystack, string, args);
+ FFI_ARG_OR_RETURN (sc, char *, needle, string, args);
+ FFI_ARGS_DONE_OR_RETURN (sc, args);
+ FFI_RETURN_POINTER (sc, strstr (haystack, needle) ? sc->T : sc->F);
+}
+
gpg_error_t
ffi_list2argv (scheme *sc, pointer list, char ***argv, size_t *len)
{
int i;
*len = sc->vptr->list_length (sc, list);
*argv = xtrycalloc (*len + 1, sizeof **argv);
if (*argv == NULL)
return gpg_error_from_syserror ();
for (i = 0; sc->vptr->is_pair (list); list = sc->vptr->pair_cdr (list))
{
if (sc->vptr->is_string (sc->vptr->pair_car (list)))
(*argv)[i++] = sc->vptr->string_value (sc->vptr->pair_car (list));
else if (sc->vptr->is_symbol (sc->vptr->pair_car (list)))
(*argv)[i++] = sc->vptr->symname (sc->vptr->pair_car (list));
else
{
xfree (*argv);
*argv = NULL;
*len = i;
return gpg_error (GPG_ERR_INV_VALUE);
}
}
(*argv)[i] = NULL;
return 0;
}
gpg_error_t
ffi_list2intv (scheme *sc, pointer list, int **intv, size_t *len)
{
int i;
*len = sc->vptr->list_length (sc, list);
*intv = xtrycalloc (*len, sizeof **intv);
if (*intv == NULL)
return gpg_error_from_syserror ();
for (i = 0; sc->vptr->is_pair (list); list = sc->vptr->pair_cdr (list))
{
if (sc->vptr->is_number (sc->vptr->pair_car (list)))
(*intv)[i++] = sc->vptr->ivalue (sc->vptr->pair_car (list));
else
{
xfree (*intv);
*intv = NULL;
*len = i;
return gpg_error (GPG_ERR_INV_VALUE);
}
}
return 0;
}
const char *
ffi_schemify_name (const char *s, int macro)
{
char *n = strdup (s), *p;
if (n == NULL)
return s;
for (p = n; *p; p++)
{
*p = (char) tolower (*p);
/* We convert _ to - in identifiers. We allow, however, for
function names to start with a leading _. The functions in
this namespace are not yet finalized and might change or
vanish without warning. Use them with care. */
if (! macro
&& p != n
&& *p == '_')
*p = '-';
}
return n;
}
pointer
ffi_sprintf (scheme *sc, const char *format, ...)
{
pointer result;
va_list listp;
char *expression;
int size, written;
va_start (listp, format);
size = vsnprintf (NULL, 0, format, listp);
va_end (listp);
expression = xtrymalloc (size + 1);
if (expression == NULL)
return NULL;
va_start (listp, format);
written = vsnprintf (expression, size + 1, format, listp);
va_end (listp);
assert (size == written);
result = sc->vptr->mk_string (sc, expression);
xfree (expression);
return result;
}
void
ffi_scheme_eval (scheme *sc, const char *format, ...)
{
va_list listp;
char *expression;
int size, written;
va_start (listp, format);
size = vsnprintf (NULL, 0, format, listp);
va_end (listp);
expression = xtrymalloc (size + 1);
if (expression == NULL)
return;
va_start (listp, format);
written = vsnprintf (expression, size + 1, format, listp);
va_end (listp);
assert (size == written);
sc->vptr->load_string (sc, expression);
xfree (expression);
}
gpg_error_t
ffi_init (scheme *sc, const char *argv0, int argc, const char **argv)
{
int i;
pointer args = sc->NIL;
/* bitwise arithmetic */
ffi_define_function (sc, logand);
ffi_define_function (sc, logior);
ffi_define_function (sc, logxor);
ffi_define_function (sc, lognot);
/* libc. */
ffi_define_constant (sc, O_RDONLY);
ffi_define_constant (sc, O_WRONLY);
ffi_define_constant (sc, O_RDWR);
ffi_define_constant (sc, O_CREAT);
ffi_define_constant (sc, O_APPEND);
#ifndef O_BINARY
# define O_BINARY 0
#endif
#ifndef O_TEXT
# define O_TEXT 0
#endif
ffi_define_constant (sc, O_BINARY);
ffi_define_constant (sc, O_TEXT);
ffi_define_constant (sc, STDIN_FILENO);
ffi_define_constant (sc, STDOUT_FILENO);
ffi_define_constant (sc, STDERR_FILENO);
ffi_define_function (sc, sleep);
ffi_define_function (sc, usleep);
ffi_define_function (sc, chdir);
ffi_define_function (sc, strerror);
ffi_define_function (sc, getenv);
ffi_define_function (sc, setenv);
ffi_define_function (sc, exit);
ffi_define_function (sc, open);
ffi_define_function (sc, fdopen);
ffi_define_function (sc, close);
ffi_define_function (sc, mkdtemp);
ffi_define_function (sc, unlink);
ffi_define_function (sc, unlink_recursively);
ffi_define_function (sc, rename);
ffi_define_function (sc, getcwd);
ffi_define_function (sc, mkdir);
ffi_define_function (sc, rmdir);
/* Process management. */
ffi_define_function (sc, spawn_process);
ffi_define_function (sc, spawn_process_fd);
ffi_define_function (sc, wait_process);
ffi_define_function (sc, wait_processes);
ffi_define_function (sc, pipe);
ffi_define_function (sc, inbound_pipe);
ffi_define_function (sc, outbound_pipe);
/* estream functions. */
ffi_define_function_name (sc, "es-fclose", es_fclose);
ffi_define_function_name (sc, "es-read", es_read);
ffi_define_function_name (sc, "es-feof", es_feof);
ffi_define_function_name (sc, "es-write", es_write);
/* Test helper functions. */
ffi_define_function (sc, file_equal);
ffi_define_function (sc, splice);
+ ffi_define_function (sc, string_index);
+ ffi_define_function (sc, string_rindex);
+ ffi_define_function_name (sc, "string-contains?", string_contains);
/* User interface. */
ffi_define_function (sc, flush_stdio);
ffi_define_function (sc, prompt);
/* Configuration. */
ffi_define (sc, "*verbose*", sc->vptr->mk_integer (sc, verbose));
ffi_define (sc, "*argv0*", sc->vptr->mk_string (sc, argv0));
for (i = argc - 1; i >= 0; i--)
{
pointer value = sc->vptr->mk_string (sc, argv[i]);
args = (sc->vptr->cons) (sc, value, args);
}
ffi_define (sc, "*args*", args);
#if _WIN32
ffi_define (sc, "*pathsep*", sc->vptr->mk_character (sc, ';'));
#else
ffi_define (sc, "*pathsep*", sc->vptr->mk_character (sc, ':'));
#endif
ffi_define (sc, "*stdin*",
sc->vptr->mk_port_from_file (sc, stdin, port_input));
ffi_define (sc, "*stdout*",
sc->vptr->mk_port_from_file (sc, stdout, port_output));
ffi_define (sc, "*stderr*",
sc->vptr->mk_port_from_file (sc, stderr, port_output));
return 0;
}
diff --git a/ffi.scm b/ffi.scm
index d0b8a99..7c2f93a 100644
--- a/ffi.scm
+++ b/ffi.scm
@@ -1,40 +1,44 @@
;; FFI interface for TinySCHEME.
;;
;; Copyright (C) 2016 g10 Code GmbH
;;
;; This file is part of GnuPG.
;;
;; GnuPG 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.
;;
;; GnuPG 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; if not, see .
;; Foreign function wrapper. Expects F to return a list with the
;; first element being the `error_t' value returned by the foreign
;; function. The error is thrown, or the cdr of the result is
;; returned.
(define (ffi-apply name f args)
(let ((result (apply f args)))
(cond
((string? result)
(ffi-fail name args result))
((not (= (car result) 0))
(ffi-fail name args (strerror (car result))))
((and (= (car result) 0) (pair? (cdr result))) (cadr result))
((= (car result) 0) '())
(else
(throw (list "Result violates FFI calling convention: " result))))))
(define (ffi-fail name args message)
(let ((args' (open-output-string)))
(write (cons (string->symbol name) args) args')
(throw (string-append
(get-output-string args') ": " message))))
+
+;; Pseudo-definitions for foreign functions. Evaluates to no code,
+;; but serves as documentation.
+(macro (ffi-define form))
diff --git a/lib.scm b/lib.scm
index 871cc8f..48f53ea 100644
--- a/lib.scm
+++ b/lib.scm
@@ -1,163 +1,163 @@
;; Additional library functions for TinySCHEME.
;;
;; Copyright (C) 2016 g10 Code GmbH
;;
;; This file is part of GnuPG.
;;
;; GnuPG 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.
;;
;; GnuPG 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; if not, see .
(macro (assert form)
`(if (not ,(cadr form))
(begin
(display (list "Assertion failed:" (quote ,(cadr form))))
(newline)
(exit 1))))
(assert #t)
(define (filter pred lst)
(cond ((null? lst) '())
((pred (car lst))
(cons (car lst) (filter pred (cdr lst))))
(else (filter pred (cdr lst)))))
(define (any p l)
(cond ((null? l) #f)
((p (car l)) #t)
(else (any p (cdr l)))))
(define (all p l)
(cond ((null? l) #t)
((not (p (car l))) #f)
(else (all p (cdr l)))))
;; Is PREFIX a prefix of S?
(define (string-prefix? s prefix)
(and (>= (string-length s) (string-length prefix))
(string=? prefix (substring s 0 (string-length prefix)))))
(assert (string-prefix? "Scheme" "Sch"))
;; Is SUFFIX a suffix of S?
(define (string-suffix? s suffix)
(and (>= (string-length s) (string-length suffix))
(string=? suffix (substring s (- (string-length s)
(string-length suffix))
(string-length s)))))
(assert (string-suffix? "Scheme" "eme"))
-;; Locate the first occurrence of needle in haystack.
-(define (string-index haystack needle)
- (define (index i haystack needle)
- (if (= (length haystack) 0)
- #f
- (if (char=? (car haystack) needle)
- i
- (index (+ i 1) (cdr haystack) needle))))
- (index 0 (string->list haystack) needle))
-
-;; Locate the last occurrence of needle in haystack.
-(define (string-rindex haystack needle)
- (let ((rindex (string-index (list->string (reverse (string->list haystack)))
- needle)))
- (if rindex (- (string-length haystack) rindex 1) #f)))
+;; Locate the first occurrence of needle in haystack starting at offset.
+(ffi-define (string-index haystack needle [offset]))
+(assert (= 2 (string-index "Hallo" #\l)))
+(assert (= 3 (string-index "Hallo" #\l 3)))
+(assert (equal? #f (string-index "Hallo" #\.)))
+
+;; Locate the last occurrence of needle in haystack starting at offset.
+(ffi-define (string-rindex haystack needle [offset]))
+(assert (= 3 (string-rindex "Hallo" #\l)))
+(assert (equal? #f (string-rindex "Hallo" #\a 2)))
+(assert (equal? #f (string-rindex "Hallo" #\.)))
;; Split haystack at delimiter at most n times.
(define (string-splitn haystack delimiter n)
- (define (split acc haystack delimiter n)
- (if (= (string-length haystack) 0)
- (reverse acc)
- (let ((i (string-index haystack delimiter)))
- (if (not (or (eq? i #f) (= 0 n)))
- (split (cons (substring haystack 0 i) acc)
- (substring haystack (+ i 1) (string-length haystack))
- delimiter (- n 1))
- (split (cons haystack acc) "" delimiter 0)
- ))))
- (split '() haystack delimiter n))
+ (let ((length (string-length haystack)))
+ (define (split acc delimiter offset n)
+ (if (>= offset length)
+ (reverse acc)
+ (let ((i (string-index haystack delimiter offset)))
+ (if (or (eq? i #f) (= 0 n))
+ (reverse (cons (substring haystack offset length) acc))
+ (split (cons (substring haystack offset i) acc)
+ delimiter (+ i 1) (- n 1))))))
+ (split '() delimiter 0 n)))
+(assert (= 2 (length (string-splitn "foo:bar:baz" #\: 1))))
+(assert (string=? "foo" (car (string-splitn "foo:bar:baz" #\: 1))))
+(assert (string=? "bar:baz" (cadr (string-splitn "foo:bar:baz" #\: 1))))
;; Split haystack at delimiter.
(define (string-split haystack delimiter)
(string-splitn haystack delimiter -1))
+(assert (= 3 (length (string-split "foo:bar:baz" #\:))))
+(assert (string=? "foo" (car (string-split "foo:bar:baz" #\:))))
+(assert (string=? "bar" (cadr (string-split "foo:bar:baz" #\:))))
+(assert (string=? "baz" (caddr (string-split "foo:bar:baz" #\:))))
;; Trim the prefix of S containing only characters that make PREDICATE
-;; true. For example (string-ltrim char-whitespace? " foo") =>
-;; "foo".
+;; true.
(define (string-ltrim predicate s)
(let loop ((s' (string->list s)))
(if (predicate (car s'))
(loop (cdr s'))
(list->string s'))))
+(assert (string=? "foo" (string-ltrim char-whitespace? " foo")))
;; Trim the suffix of S containing only characters that make PREDICATE
;; true.
(define (string-rtrim predicate s)
(let loop ((s' (reverse (string->list s))))
(if (predicate (car s'))
(loop (cdr s'))
(list->string (reverse s')))))
+(assert (string=? "foo" (string-rtrim char-whitespace? "foo ")))
;; Trim both the prefix and suffix of S containing only characters
;; that make PREDICATE true.
(define (string-trim predicate s)
(string-ltrim predicate (string-rtrim predicate s)))
+(assert (string=? "foo" (string-trim char-whitespace? " foo ")))
-(define (string-contains? s contained)
- (let loop ((offset 0))
- (if (<= (+ offset (string-length contained)) (string-length s))
- (if (string=? (substring s offset (+ offset (string-length contained)))
- contained)
- #t
- (loop (+ 1 offset)))
- #f)))
+;; Check if needle is contained in haystack.
+(ffi-define (string-contains? haystack needle))
+(assert (string-contains? "Hallo" "llo"))
+(assert (not (string-contains? "Hallo" "olla")))
(define (echo . msg)
(for-each (lambda (x) (display x) (display " ")) msg)
(newline))
;; Read a word from port P.
(define (read-word . p)
(list->string
(let f ()
(let ((c (apply peek-char p)))
(cond
((eof-object? c) '())
((char-alphabetic? c)
(apply read-char p)
(cons c (f)))
(else
(apply read-char p)
'()))))))
;; Read a line from port P.
(define (read-line . p)
(list->string
(let f ()
(let ((c (apply peek-char p)))
(cond
((eof-object? c) '())
((char=? c #\newline)
(apply read-char p)
'())
(else
(apply read-char p)
(cons c (f))))))))
;; Read everything from port P.
(define (read-all . p)
- (list->string
- (let f ()
- (let ((c (apply peek-char p)))
- (cond
- ((eof-object? c) '())
- (else (apply read-char p)
- (cons c (f))))))))
+ (let loop ((acc (open-output-string)))
+ (let ((c (apply peek-char p)))
+ (cond
+ ((eof-object? c) (get-output-string acc))
+ (else
+ (write-char (apply read-char p) acc)
+ (loop acc))))))