867 lines
21 KiB
C
867 lines
21 KiB
C
|
/*
|
||
|
* Copyright (c) 2001 by The XFree86 Project, Inc.
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the "Software"),
|
||
|
* to deal in the Software without restriction, including without limitation
|
||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||
|
* Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
* THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||
|
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
* SOFTWARE.
|
||
|
*
|
||
|
* Except as contained in this notice, the name of the XFree86 Project shall
|
||
|
* not be used in advertising or otherwise to promote the sale, use or other
|
||
|
* dealings in this Software without prior written authorization from the
|
||
|
* XFree86 Project.
|
||
|
*
|
||
|
* Author: Paulo César Pereira de Andrade
|
||
|
*/
|
||
|
|
||
|
/* $XFree86: xc/programs/xedit/lisp/stream.c,v 1.21tsi Exp $ */
|
||
|
|
||
|
#include "lisp/read.h"
|
||
|
#include "lisp/stream.h"
|
||
|
#include "lisp/pathname.h"
|
||
|
#include "lisp/write.h"
|
||
|
#include "lisp/private.h"
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <signal.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/wait.h>
|
||
|
|
||
|
/*
|
||
|
* Initialization
|
||
|
*/
|
||
|
#define DIR_PROBE 0
|
||
|
#define DIR_INPUT 1
|
||
|
#define DIR_OUTPUT 2
|
||
|
#define DIR_IO 3
|
||
|
|
||
|
#define EXT_NIL 0
|
||
|
#define EXT_ERROR 1
|
||
|
#define EXT_NEW_VERSION 2
|
||
|
#define EXT_RENAME 3
|
||
|
#define EXT_RENAME_DELETE 4
|
||
|
#define EXT_OVERWRITE 5
|
||
|
#define EXT_APPEND 6
|
||
|
#define EXT_SUPERSEDE 7
|
||
|
|
||
|
#define NOEXT_NIL 0
|
||
|
#define NOEXT_ERROR 1
|
||
|
#define NOEXT_CREATE 2
|
||
|
#define NOEXT_NOTHING 3
|
||
|
|
||
|
extern char **environ;
|
||
|
|
||
|
LispObj *Oopen, *Oclose, *Otruename;
|
||
|
|
||
|
LispObj *Kif_does_not_exist, *Kprobe, *Kinput, *Koutput, *Kio,
|
||
|
*Knew_version, *Krename, *Krename_and_delete, *Koverwrite,
|
||
|
*Kappend, *Ksupersede, *Kcreate;
|
||
|
|
||
|
/*
|
||
|
* Implementation
|
||
|
*/
|
||
|
void
|
||
|
LispStreamInit(void)
|
||
|
{
|
||
|
Oopen = STATIC_ATOM("OPEN");
|
||
|
Oclose = STATIC_ATOM("CLOSE");
|
||
|
Otruename = STATIC_ATOM("TRUENAME");
|
||
|
|
||
|
Kif_does_not_exist = KEYWORD("IF-DOES-NOT-EXIST");
|
||
|
Kprobe = KEYWORD("PROBE");
|
||
|
Kinput = KEYWORD("INPUT");
|
||
|
Koutput = KEYWORD("OUTPUT");
|
||
|
Kio = KEYWORD("IO");
|
||
|
Knew_version = KEYWORD("NEW-VERSION");
|
||
|
Krename = KEYWORD("RENAME");
|
||
|
Krename_and_delete = KEYWORD("RENAME-AND-DELETE");
|
||
|
Koverwrite = KEYWORD("OVERWRITE");
|
||
|
Kappend = KEYWORD("APPEND");
|
||
|
Ksupersede = KEYWORD("SUPERSEDE");
|
||
|
Kcreate = KEYWORD("CREATE");
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_DeleteFile(LispBuiltin *builtin)
|
||
|
/*
|
||
|
delete-file filename
|
||
|
*/
|
||
|
{
|
||
|
GC_ENTER();
|
||
|
LispObj *filename;
|
||
|
|
||
|
filename = ARGUMENT(0);
|
||
|
|
||
|
if (STRINGP(filename)) {
|
||
|
filename = APPLY1(Oparse_namestring, filename);
|
||
|
GC_PROTECT(filename);
|
||
|
}
|
||
|
else if (STREAMP(filename)) {
|
||
|
if (filename->data.stream.type != LispStreamFile)
|
||
|
LispDestroy("%s: %s is not a FILE-STREAM",
|
||
|
STRFUN(builtin), STROBJ(filename));
|
||
|
filename = filename->data.stream.pathname;
|
||
|
}
|
||
|
else {
|
||
|
CHECK_PATHNAME(filename);
|
||
|
}
|
||
|
GC_LEAVE();
|
||
|
|
||
|
return (LispUnlink(THESTR(CAR(filename->data.pathname))) ? NIL : T);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_RenameFile(LispBuiltin *builtin)
|
||
|
/*
|
||
|
rename-file filename new-name
|
||
|
*/
|
||
|
{
|
||
|
int code;
|
||
|
GC_ENTER();
|
||
|
char *from, *to;
|
||
|
LispObj *old_truename, *new_truename;
|
||
|
|
||
|
LispObj *filename, *new_name;
|
||
|
|
||
|
new_name = ARGUMENT(1);
|
||
|
filename = ARGUMENT(0);
|
||
|
|
||
|
if (STRINGP(filename)) {
|
||
|
filename = APPLY1(Oparse_namestring, filename);
|
||
|
GC_PROTECT(filename);
|
||
|
}
|
||
|
else if (STREAMP(filename)) {
|
||
|
if (filename->data.stream.type != LispStreamFile)
|
||
|
LispDestroy("%s: %s is not a FILE-STREAM",
|
||
|
STRFUN(builtin), STROBJ(filename));
|
||
|
filename = filename->data.stream.pathname;
|
||
|
}
|
||
|
else {
|
||
|
CHECK_PATHNAME(filename);
|
||
|
}
|
||
|
old_truename = APPLY1(Otruename, filename);
|
||
|
GC_PROTECT(old_truename);
|
||
|
|
||
|
if (STRINGP(new_name)) {
|
||
|
new_name = APPLY3(Oparse_namestring, new_name, NIL, filename);
|
||
|
GC_PROTECT(new_name);
|
||
|
}
|
||
|
else {
|
||
|
CHECK_PATHNAME(new_name);
|
||
|
}
|
||
|
|
||
|
from = THESTR(CAR(filename->data.pathname));
|
||
|
to = THESTR(CAR(new_name->data.pathname));
|
||
|
code = LispRename(from, to);
|
||
|
if (code)
|
||
|
LispDestroy("%s: rename(%s, %s): %s",
|
||
|
STRFUN(builtin), from, to, strerror(errno));
|
||
|
GC_LEAVE();
|
||
|
|
||
|
new_truename = APPLY1(Otruename, new_name);
|
||
|
RETURN_COUNT = 2;
|
||
|
RETURN(0) = old_truename;
|
||
|
RETURN(1) = new_truename;
|
||
|
|
||
|
return (new_name);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_Streamp(LispBuiltin *builtin)
|
||
|
/*
|
||
|
streamp object
|
||
|
*/
|
||
|
{
|
||
|
LispObj *object;
|
||
|
|
||
|
object = ARGUMENT(0);
|
||
|
|
||
|
return (STREAMP(object) ? T : NIL);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_InputStreamP(LispBuiltin *builtin)
|
||
|
/*
|
||
|
input-stream-p stream
|
||
|
*/
|
||
|
{
|
||
|
LispObj *stream;
|
||
|
|
||
|
stream = ARGUMENT(0);
|
||
|
|
||
|
CHECK_STREAM(stream);
|
||
|
|
||
|
return (stream->data.stream.readable ? T : NIL);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_OpenStreamP(LispBuiltin *builtin)
|
||
|
/*
|
||
|
open-stream-p stream
|
||
|
*/
|
||
|
{
|
||
|
LispObj *stream;
|
||
|
|
||
|
stream = ARGUMENT(0);
|
||
|
|
||
|
CHECK_STREAM(stream);
|
||
|
|
||
|
return (stream->data.stream.readable || stream->data.stream.writable ?
|
||
|
T : NIL);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_OutputStreamP(LispBuiltin *builtin)
|
||
|
/*
|
||
|
output-stream-p stream
|
||
|
*/
|
||
|
{
|
||
|
LispObj *stream;
|
||
|
|
||
|
stream = ARGUMENT(0);
|
||
|
|
||
|
CHECK_STREAM(stream);
|
||
|
|
||
|
return (stream->data.stream.writable ? T : NIL);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_Open(LispBuiltin *builtin)
|
||
|
/*
|
||
|
open filename &key direction element-type if-exists if-does-not-exist external-format
|
||
|
*/
|
||
|
{
|
||
|
GC_ENTER();
|
||
|
char *string;
|
||
|
LispObj *stream = NIL;
|
||
|
int mode, flags, direction, exist, noexist, file_exist;
|
||
|
LispFile *file;
|
||
|
|
||
|
LispObj *filename, *odirection, *element_type, *if_exists,
|
||
|
*if_does_not_exist, *external_format;
|
||
|
|
||
|
external_format = ARGUMENT(5);
|
||
|
if_does_not_exist = ARGUMENT(4);
|
||
|
if_exists = ARGUMENT(3);
|
||
|
element_type = ARGUMENT(2);
|
||
|
odirection = ARGUMENT(1);
|
||
|
filename = ARGUMENT(0);
|
||
|
|
||
|
if (STRINGP(filename)) {
|
||
|
filename = APPLY1(Oparse_namestring, filename);
|
||
|
GC_PROTECT(filename);
|
||
|
}
|
||
|
else if (STREAMP(filename)) {
|
||
|
if (filename->data.stream.type != LispStreamFile)
|
||
|
LispDestroy("%s: %s is not a FILE-STREAM",
|
||
|
STRFUN(builtin), STROBJ(filename));
|
||
|
filename = filename->data.stream.pathname;
|
||
|
}
|
||
|
else {
|
||
|
CHECK_PATHNAME(filename);
|
||
|
}
|
||
|
|
||
|
if (odirection != UNSPEC) {
|
||
|
direction = -1;
|
||
|
if (KEYWORDP(odirection)) {
|
||
|
if (odirection == Kprobe)
|
||
|
direction = DIR_PROBE;
|
||
|
else if (odirection == Kinput)
|
||
|
direction = DIR_INPUT;
|
||
|
else if (odirection == Koutput)
|
||
|
direction = DIR_OUTPUT;
|
||
|
else if (odirection == Kio)
|
||
|
direction = DIR_IO;
|
||
|
}
|
||
|
if (direction == -1)
|
||
|
LispDestroy("%s: bad :DIRECTION %s",
|
||
|
STRFUN(builtin), STROBJ(odirection));
|
||
|
}
|
||
|
else
|
||
|
direction = DIR_INPUT;
|
||
|
|
||
|
if (element_type != UNSPEC) {
|
||
|
/* just check argument... */
|
||
|
if (SYMBOLP(element_type) &&
|
||
|
ATOMID(element_type) == Scharacter)
|
||
|
; /* do nothing */
|
||
|
else if (KEYWORDP(element_type) &&
|
||
|
ATOMID(element_type) == Sdefault)
|
||
|
; /* do nothing */
|
||
|
else
|
||
|
LispDestroy("%s: only :%s and %s supported for :ELEMENT-TYPE, not %s",
|
||
|
STRFUN(builtin), Sdefault, Scharacter, STROBJ(element_type));
|
||
|
}
|
||
|
|
||
|
if (if_exists != UNSPEC) {
|
||
|
exist = -1;
|
||
|
if (if_exists == NIL)
|
||
|
exist = EXT_NIL;
|
||
|
else if (KEYWORDP(if_exists)) {
|
||
|
if (if_exists == Kerror)
|
||
|
exist = EXT_ERROR;
|
||
|
else if (if_exists == Knew_version)
|
||
|
exist = EXT_NEW_VERSION;
|
||
|
else if (if_exists == Krename)
|
||
|
exist = EXT_RENAME;
|
||
|
else if (if_exists == Krename_and_delete)
|
||
|
exist = EXT_RENAME_DELETE;
|
||
|
else if (if_exists == Koverwrite)
|
||
|
exist = EXT_OVERWRITE;
|
||
|
else if (if_exists == Kappend)
|
||
|
exist = EXT_APPEND;
|
||
|
else if (if_exists == Ksupersede)
|
||
|
exist = EXT_SUPERSEDE;
|
||
|
}
|
||
|
if (exist == -1)
|
||
|
LispDestroy("%s: bad :IF-EXISTS %s",
|
||
|
STRFUN(builtin), STROBJ(if_exists));
|
||
|
}
|
||
|
else
|
||
|
exist = EXT_ERROR;
|
||
|
|
||
|
if (if_does_not_exist != UNSPEC) {
|
||
|
noexist = -1;
|
||
|
if (if_does_not_exist == NIL)
|
||
|
noexist = NOEXT_NIL;
|
||
|
if (KEYWORDP(if_does_not_exist)) {
|
||
|
if (if_does_not_exist == Kerror)
|
||
|
noexist = NOEXT_ERROR;
|
||
|
else if (if_does_not_exist == Kcreate)
|
||
|
noexist = NOEXT_CREATE;
|
||
|
}
|
||
|
if (noexist == -1)
|
||
|
LispDestroy("%s: bad :IF-DOES-NO-EXISTS %s",
|
||
|
STRFUN(builtin), STROBJ(if_does_not_exist));
|
||
|
}
|
||
|
else
|
||
|
noexist = direction != DIR_INPUT ? NOEXT_NOTHING : NOEXT_ERROR;
|
||
|
|
||
|
if (external_format != UNSPEC) {
|
||
|
/* just check argument... */
|
||
|
if (SYMBOLP(external_format) &&
|
||
|
ATOMID(external_format) == Scharacter)
|
||
|
; /* do nothing */
|
||
|
else if (KEYWORDP(external_format) &&
|
||
|
ATOMID(external_format) == Sdefault)
|
||
|
; /* do nothing */
|
||
|
else
|
||
|
LispDestroy("%s: only :%s and %s supported for :EXTERNAL-FORMAT, not %s",
|
||
|
STRFUN(builtin), Sdefault, Scharacter, STROBJ(external_format));
|
||
|
}
|
||
|
|
||
|
/* string representation of pathname */
|
||
|
string = THESTR(CAR(filename->data.pathname));
|
||
|
mode = 0;
|
||
|
|
||
|
file_exist = access(string, F_OK) == 0;
|
||
|
if (file_exist) {
|
||
|
if (exist == EXT_NIL) {
|
||
|
GC_LEAVE();
|
||
|
return (NIL);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (noexist == NOEXT_NIL) {
|
||
|
GC_LEAVE();
|
||
|
return (NIL);
|
||
|
}
|
||
|
if (noexist == NOEXT_ERROR)
|
||
|
LispDestroy("%s: file %s does not exist",
|
||
|
STRFUN(builtin), STROBJ(CAR(filename->data.quote)));
|
||
|
else if (noexist == NOEXT_CREATE) {
|
||
|
LispFile *tmp = LispFopen(string, FILE_WRITE);
|
||
|
|
||
|
if (tmp)
|
||
|
LispFclose(tmp);
|
||
|
else
|
||
|
LispDestroy("%s: cannot create file %s",
|
||
|
STRFUN(builtin),
|
||
|
STROBJ(CAR(filename->data.quote)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (direction == DIR_OUTPUT || direction == DIR_IO) {
|
||
|
if (file_exist) {
|
||
|
if (exist == EXT_ERROR)
|
||
|
LispDestroy("%s: file %s already exists",
|
||
|
STRFUN(builtin), STROBJ(CAR(filename->data.quote)));
|
||
|
if (exist == EXT_RENAME) {
|
||
|
/* Add an ending '~' at the end of the backup file */
|
||
|
char tmp[PATH_MAX + 1];
|
||
|
|
||
|
strcpy(tmp, string);
|
||
|
if (strlen(tmp) + 1 > PATH_MAX)
|
||
|
LispDestroy("%s: backup name for %s too long",
|
||
|
STRFUN(builtin),
|
||
|
STROBJ(CAR(filename->data.quote)));
|
||
|
strcat(tmp, "~");
|
||
|
if (rename(string, tmp))
|
||
|
LispDestroy("%s: rename: %s",
|
||
|
STRFUN(builtin), strerror(errno));
|
||
|
mode |= FILE_WRITE;
|
||
|
}
|
||
|
else if (exist == EXT_OVERWRITE)
|
||
|
mode |= FILE_WRITE;
|
||
|
else if (exist == EXT_APPEND)
|
||
|
mode |= FILE_APPEND;
|
||
|
}
|
||
|
else
|
||
|
mode |= FILE_WRITE;
|
||
|
if (direction == DIR_IO)
|
||
|
mode |= FILE_IO;
|
||
|
}
|
||
|
else
|
||
|
mode |= FILE_READ;
|
||
|
|
||
|
file = LispFopen(string, mode);
|
||
|
if (file == NULL)
|
||
|
LispDestroy("%s: open: %s", STRFUN(builtin), strerror(errno));
|
||
|
|
||
|
flags = 0;
|
||
|
if (direction == DIR_PROBE) {
|
||
|
LispFclose(file);
|
||
|
file = NULL;
|
||
|
}
|
||
|
else {
|
||
|
if (direction == DIR_INPUT || direction == DIR_IO)
|
||
|
flags |= STREAM_READ;
|
||
|
if (direction == DIR_OUTPUT || direction == DIR_IO)
|
||
|
flags |= STREAM_WRITE;
|
||
|
}
|
||
|
stream = FILESTREAM(file, filename, flags);
|
||
|
GC_LEAVE();
|
||
|
|
||
|
return (stream);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_Close(LispBuiltin *builtin)
|
||
|
/*
|
||
|
close stream &key abort
|
||
|
*/
|
||
|
{
|
||
|
LispObj *stream, *oabort;
|
||
|
|
||
|
oabort = ARGUMENT(1);
|
||
|
stream = ARGUMENT(0);
|
||
|
|
||
|
CHECK_STREAM(stream);
|
||
|
|
||
|
if (stream->data.stream.readable || stream->data.stream.writable) {
|
||
|
stream->data.stream.readable = stream->data.stream.writable = 0;
|
||
|
if (stream->data.stream.type == LispStreamFile) {
|
||
|
LispFclose(stream->data.stream.source.file);
|
||
|
stream->data.stream.source.file = NULL;
|
||
|
}
|
||
|
else if (stream->data.stream.type == LispStreamPipe) {
|
||
|
if (IPSTREAMP(stream)) {
|
||
|
LispFclose(IPSTREAMP(stream));
|
||
|
IPSTREAMP(stream) = NULL;
|
||
|
}
|
||
|
if (OPSTREAMP(stream)) {
|
||
|
LispFclose(OPSTREAMP(stream));
|
||
|
OPSTREAMP(stream) = NULL;
|
||
|
}
|
||
|
if (EPSTREAMP(stream)) {
|
||
|
LispFclose(EPSTREAMP(stream));
|
||
|
EPSTREAMP(stream) = NULL;
|
||
|
}
|
||
|
if (PIDPSTREAMP(stream) > 0) {
|
||
|
kill(PIDPSTREAMP(stream),
|
||
|
oabort == UNSPEC || oabort == NIL ? SIGTERM : SIGKILL);
|
||
|
waitpid(PIDPSTREAMP(stream), NULL, 0);
|
||
|
}
|
||
|
}
|
||
|
return (T);
|
||
|
}
|
||
|
|
||
|
return (NIL);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_Listen(LispBuiltin *builtin)
|
||
|
/*
|
||
|
listen &optional input-stream
|
||
|
*/
|
||
|
{
|
||
|
LispFile *file = NULL;
|
||
|
LispObj *result = NIL;
|
||
|
|
||
|
LispObj *stream;
|
||
|
|
||
|
stream = ARGUMENT(0);
|
||
|
|
||
|
if (stream == UNSPEC)
|
||
|
stream = NIL;
|
||
|
else if (stream != NIL) {
|
||
|
CHECK_STREAM(stream);
|
||
|
}
|
||
|
else
|
||
|
stream = lisp__data.standard_input;
|
||
|
|
||
|
if (stream->data.stream.readable) {
|
||
|
switch (stream->data.stream.type) {
|
||
|
case LispStreamString:
|
||
|
if (SSTREAMP(stream)->input < SSTREAMP(stream)->length)
|
||
|
result = T;
|
||
|
break;
|
||
|
case LispStreamFile:
|
||
|
file = FSTREAMP(stream);
|
||
|
break;
|
||
|
case LispStreamStandard:
|
||
|
file = FSTREAMP(stream);
|
||
|
break;
|
||
|
case LispStreamPipe:
|
||
|
file = IPSTREAMP(stream);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (file != NULL) {
|
||
|
if (file->available || file->offset < file->length)
|
||
|
result = T;
|
||
|
else {
|
||
|
unsigned char c;
|
||
|
|
||
|
if (!file->nonblock) {
|
||
|
if (fcntl(file->descriptor, F_SETFL, O_NONBLOCK) < 0)
|
||
|
LispDestroy("%s: fcntl: %s",
|
||
|
STRFUN(builtin), strerror(errno));
|
||
|
file->nonblock = 1;
|
||
|
}
|
||
|
if (read(file->descriptor, &c, 1) == 1) {
|
||
|
LispFungetc(file, c);
|
||
|
result = T;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (result);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_MakeStringInputStream(LispBuiltin *builtin)
|
||
|
/*
|
||
|
make-string-input-stream string &optional start end
|
||
|
*/
|
||
|
{
|
||
|
char *string;
|
||
|
long start, end, length;
|
||
|
|
||
|
LispObj *ostring, *ostart, *oend, *result;
|
||
|
|
||
|
oend = ARGUMENT(2);
|
||
|
ostart = ARGUMENT(1);
|
||
|
ostring = ARGUMENT(0);
|
||
|
|
||
|
start = end = 0;
|
||
|
CHECK_STRING(ostring);
|
||
|
LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
|
||
|
&start, &end, &length);
|
||
|
string = THESTR(ostring);
|
||
|
|
||
|
if (end - start != length)
|
||
|
length = end - start;
|
||
|
result = LSTRINGSTREAM(string + start, STREAM_READ, length);
|
||
|
|
||
|
return (result);
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_MakeStringOutputStream(LispBuiltin *builtin)
|
||
|
/*
|
||
|
make-string-output-stream &key element-type
|
||
|
*/
|
||
|
{
|
||
|
LispObj *element_type;
|
||
|
|
||
|
element_type = ARGUMENT(0);
|
||
|
|
||
|
if (element_type != UNSPEC) {
|
||
|
/* just check argument... */
|
||
|
if (SYMBOLP(element_type) && ATOMID(element_type) == Scharacter)
|
||
|
; /* do nothing */
|
||
|
else if (KEYWORDP(element_type) &&
|
||
|
ATOMID(element_type) == Sdefault)
|
||
|
; /* do nothing */
|
||
|
else
|
||
|
LispDestroy("%s: only :%s and %s supported for :ELEMENT-TYPE, not %s",
|
||
|
STRFUN(builtin), Sdefault, Scharacter, STROBJ(element_type));
|
||
|
}
|
||
|
|
||
|
return (LSTRINGSTREAM("", STREAM_WRITE, 1));
|
||
|
}
|
||
|
|
||
|
LispObj *
|
||
|
Lisp_GetOutputStreamString(LispBuiltin *builtin)
|
||
|
/*
|
||
|
get-output-stream-string string-output-stream
|
||
|
*/
|
||
|
{
|
||
|
int length;
|
||
|
char *string;
|
||
|
LispObj *string_output_stream, *result;
|
||
|
|
||
|
string_output_stream = ARGUMENT(0);
|
||
|
|
||
|
if (!STREAMP(string_output_stream) ||
|
||
|
string_output_stream->data.stream.type != LispStreamString ||
|
||
|
string_output_stream->data.stream.readable ||
|
||
|
!string_output_stream->data.stream.writable)
|
||
|
LispDestroy("%s: %s is not an output string stream",
|
||
|
STRFUN(builtin), STROBJ(string_output_stream));
|
||
|
|
||
|
string = LispGetSstring(SSTREAMP(string_output_stream), &length);
|
||
|
result = LSTRING(string, length);
|
||
|
|
||
|
/* reset string */
|
||
|
SSTREAMP(string_output_stream)->output =
|
||
|
SSTREAMP(string_output_stream)->length =
|
||
|
SSTREAMP(string_output_stream)->column = 0;
|
||
|
|
||
|
return (result);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* XXX Non standard functions below
|
||
|
*/
|
||
|
LispObj *
|
||
|
Lisp_MakePipe(LispBuiltin *builtin)
|
||
|
/*
|
||
|
make-pipe command-line &key :direction :element-type :external-format
|
||
|
*/
|
||
|
{
|
||
|
char *string;
|
||
|
LispObj *stream = NIL;
|
||
|
int flags, direction;
|
||
|
LispFile *error_file;
|
||
|
LispPipe *program;
|
||
|
int ifd[2];
|
||
|
int ofd[2];
|
||
|
int efd[2];
|
||
|
char *argv[4];
|
||
|
|
||
|
LispObj *command_line, *odirection, *element_type, *external_format;
|
||
|
|
||
|
external_format = ARGUMENT(3);
|
||
|
element_type = ARGUMENT(2);
|
||
|
odirection = ARGUMENT(1);
|
||
|
command_line = ARGUMENT(0);
|
||
|
|
||
|
if (PATHNAMEP(command_line))
|
||
|
command_line = CAR(command_line->data.quote);
|
||
|
else if (!STRINGP(command_line))
|
||
|
LispDestroy("%s: %s is a bad pathname",
|
||
|
STRFUN(builtin), STROBJ(command_line));
|
||
|
|
||
|
if (odirection != UNSPEC) {
|
||
|
direction = -1;
|
||
|
if (KEYWORDP(odirection)) {
|
||
|
if (odirection == Kprobe)
|
||
|
direction = DIR_PROBE;
|
||
|
else if (odirection == Kinput)
|
||
|
direction = DIR_INPUT;
|
||
|
else if (odirection == Koutput)
|
||
|
direction = DIR_OUTPUT;
|
||
|
else if (odirection == Kio)
|
||
|
direction = DIR_IO;
|
||
|
}
|
||
|
if (direction == -1)
|
||
|
LispDestroy("%s: bad :DIRECTION %s",
|
||
|
STRFUN(builtin), STROBJ(odirection));
|
||
|
}
|
||
|
else
|
||
|
direction = DIR_INPUT;
|
||
|
|
||
|
if (element_type != UNSPEC) {
|
||
|
/* just check argument... */
|
||
|
if (SYMBOLP(element_type) && ATOMID(element_type) == Scharacter)
|
||
|
; /* do nothing */
|
||
|
else if (KEYWORDP(element_type) && ATOMID(element_type) == Sdefault)
|
||
|
; /* do nothing */
|
||
|
else
|
||
|
LispDestroy("%s: only :%s and %s supported for :ELEMENT-TYPE, not %s",
|
||
|
STRFUN(builtin), Sdefault, Scharacter, STROBJ(element_type));
|
||
|
}
|
||
|
|
||
|
if (external_format != UNSPEC) {
|
||
|
/* just check argument... */
|
||
|
if (SYMBOLP(external_format) && ATOMID(external_format) == Scharacter)
|
||
|
; /* do nothing */
|
||
|
else if (KEYWORDP(external_format) &&
|
||
|
ATOMID(external_format) == Sdefault)
|
||
|
; /* do nothing */
|
||
|
else
|
||
|
LispDestroy("%s: only :%s and %s supported for :EXTERNAL-FORMAT, not %s",
|
||
|
STRFUN(builtin), Sdefault, Scharacter, STROBJ(external_format));
|
||
|
}
|
||
|
|
||
|
string = THESTR(command_line);
|
||
|
program = LispMalloc(sizeof(LispPipe));
|
||
|
if (direction != DIR_PROBE) {
|
||
|
argv[0] = "sh";
|
||
|
argv[1] = "-c";
|
||
|
argv[2] = string;
|
||
|
argv[3] = NULL;
|
||
|
pipe(ifd);
|
||
|
pipe(ofd);
|
||
|
pipe(efd);
|
||
|
if ((program->pid = fork()) == 0) {
|
||
|
close(0);
|
||
|
close(1);
|
||
|
close(2);
|
||
|
dup2(ofd[0], 0);
|
||
|
dup2(ifd[1], 1);
|
||
|
dup2(efd[1], 2);
|
||
|
close(ifd[0]);
|
||
|
close(ifd[1]);
|
||
|
close(ofd[0]);
|
||
|
close(ofd[1]);
|
||
|
close(efd[0]);
|
||
|
close(efd[1]);
|
||
|
execve("/bin/sh", argv, environ);
|
||
|
exit(-1);
|
||
|
}
|
||
|
else if (program->pid < 0)
|
||
|
LispDestroy("%s: fork: %s", STRFUN(builtin), strerror(errno));
|
||
|
|
||
|
program->input = LispFdopen(ifd[0], FILE_READ | FILE_UNBUFFERED);
|
||
|
close(ifd[1]);
|
||
|
program->output = LispFdopen(ofd[1], FILE_WRITE | FILE_UNBUFFERED);
|
||
|
close(ofd[0]);
|
||
|
error_file = LispFdopen(efd[0], FILE_READ | FILE_UNBUFFERED);
|
||
|
close(efd[1]);
|
||
|
}
|
||
|
else {
|
||
|
program->pid = -1;
|
||
|
program->input = program->output = error_file = NULL;
|
||
|
}
|
||
|
|
||
|
flags = direction == DIR_PROBE ? 0 : STREAM_READ;
|
||
|
program->errorp = FILESTREAM(error_file, command_line, flags);
|
||
|
|
||
|
flags = 0;
|
||
|
if (direction != DIR_PROBE) {
|
||
|
if (direction == DIR_INPUT || direction == DIR_IO)
|
||
|
flags |= STREAM_READ;
|
||
|
if (direction == DIR_OUTPUT || direction == DIR_IO)
|
||
|
flags |= STREAM_WRITE;
|
||
|
}
|
||
|
stream = PIPESTREAM(program, command_line, flags);
|
||
|
LispMused(program);
|
||
|
|
||
|
return (stream);
|
||
|
}
|
||
|
|
||
|
/* Helper function, primarily for use with the xt module
|
||
|
*/
|
||
|
LispObj *
|
||
|
Lisp_PipeBroken(LispBuiltin *builtin)
|
||
|
/*
|
||
|
pipe-broken pipe-stream
|
||
|
*/
|
||
|
{
|
||
|
int pid, status, retval;
|
||
|
LispObj *result = NIL;
|
||
|
|
||
|
LispObj *pipe_stream;
|
||
|
|
||
|
pipe_stream = ARGUMENT(0);
|
||
|
|
||
|
if (!STREAMP(pipe_stream) ||
|
||
|
pipe_stream->data.stream.type != LispStreamPipe)
|
||
|
LispDestroy("%s: %s is not a pipe stream",
|
||
|
STRFUN(builtin), STROBJ(pipe_stream));
|
||
|
|
||
|
if ((pid = PIDPSTREAMP(pipe_stream)) > 0) {
|
||
|
retval = waitpid(pid, &status, WNOHANG | WUNTRACED);
|
||
|
if (retval == pid || (retval == -1 && errno == ECHILD))
|
||
|
result = T;
|
||
|
}
|
||
|
|
||
|
return (result);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Helper function, so that it is not required to redirect error output
|
||
|
*/
|
||
|
LispObj *
|
||
|
Lisp_PipeErrorStream(LispBuiltin *builtin)
|
||
|
/*
|
||
|
pipe-error-stream pipe-stream
|
||
|
*/
|
||
|
{
|
||
|
LispObj *pipe_stream;
|
||
|
|
||
|
pipe_stream = ARGUMENT(0);
|
||
|
|
||
|
if (!STREAMP(pipe_stream) ||
|
||
|
pipe_stream->data.stream.type != LispStreamPipe)
|
||
|
LispDestroy("%s: %s is not a pipe stream",
|
||
|
STRFUN(builtin), STROBJ(pipe_stream));
|
||
|
|
||
|
return (pipe_stream->data.stream.source.program->errorp);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Helper function, primarily for use with the xt module
|
||
|
*/
|
||
|
LispObj *
|
||
|
Lisp_PipeInputDescriptor(LispBuiltin *builtin)
|
||
|
/*
|
||
|
pipe-input-descriptor pipe-stream
|
||
|
*/
|
||
|
{
|
||
|
LispObj *pipe_stream;
|
||
|
|
||
|
pipe_stream = ARGUMENT(0);
|
||
|
|
||
|
if (!STREAMP(pipe_stream) ||
|
||
|
pipe_stream->data.stream.type != LispStreamPipe)
|
||
|
LispDestroy("%s: %s is not a pipe stream",
|
||
|
STRFUN(builtin), STROBJ(pipe_stream));
|
||
|
if (!IPSTREAMP(pipe_stream))
|
||
|
LispDestroy("%s: pipe %s is unreadable",
|
||
|
STRFUN(builtin), STROBJ(pipe_stream));
|
||
|
|
||
|
return (INTEGER(LispFileno(IPSTREAMP(pipe_stream))));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Helper function, primarily for use with the xt module
|
||
|
*/
|
||
|
LispObj *
|
||
|
Lisp_PipeErrorDescriptor(LispBuiltin *builtin)
|
||
|
/*
|
||
|
pipe-error-descriptor pipe-stream
|
||
|
*/
|
||
|
{
|
||
|
LispObj *pipe_stream;
|
||
|
|
||
|
pipe_stream = ARGUMENT(0);
|
||
|
|
||
|
if (!STREAMP(pipe_stream) ||
|
||
|
pipe_stream->data.stream.type != LispStreamPipe)
|
||
|
LispDestroy("%s: %s is not a pipe stream",
|
||
|
STRFUN(builtin), STROBJ(pipe_stream));
|
||
|
if (!EPSTREAMP(pipe_stream))
|
||
|
LispDestroy("%s: pipe %s is closed",
|
||
|
STRFUN(builtin), STROBJ(pipe_stream));
|
||
|
|
||
|
return (INTEGER(LispFileno(EPSTREAMP(pipe_stream))));
|
||
|
}
|