stream.c revision f765521f
15dfecf96Smrg/*
25dfecf96Smrg * Copyright (c) 2001 by The XFree86 Project, Inc.
35dfecf96Smrg *
45dfecf96Smrg * Permission is hereby granted, free of charge, to any person obtaining a
55dfecf96Smrg * copy of this software and associated documentation files (the "Software"),
65dfecf96Smrg * to deal in the Software without restriction, including without limitation
75dfecf96Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
85dfecf96Smrg * and/or sell copies of the Software, and to permit persons to whom the
95dfecf96Smrg * Software is furnished to do so, subject to the following conditions:
105dfecf96Smrg *
115dfecf96Smrg * The above copyright notice and this permission notice shall be included in
125dfecf96Smrg * all copies or substantial portions of the Software.
135dfecf96Smrg *
145dfecf96Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
155dfecf96Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
165dfecf96Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
175dfecf96Smrg * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
185dfecf96Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
195dfecf96Smrg * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
205dfecf96Smrg * SOFTWARE.
215dfecf96Smrg *
225dfecf96Smrg * Except as contained in this notice, the name of the XFree86 Project shall
235dfecf96Smrg * not be used in advertising or otherwise to promote the sale, use or other
245dfecf96Smrg * dealings in this Software without prior written authorization from the
255dfecf96Smrg * XFree86 Project.
265dfecf96Smrg *
275dfecf96Smrg * Author: Paulo César Pereira de Andrade
285dfecf96Smrg */
295dfecf96Smrg
305dfecf96Smrg/* $XFree86: xc/programs/xedit/lisp/stream.c,v 1.21tsi Exp $ */
315dfecf96Smrg
325dfecf96Smrg#include "lisp/read.h"
335dfecf96Smrg#include "lisp/stream.h"
345dfecf96Smrg#include "lisp/pathname.h"
355dfecf96Smrg#include "lisp/write.h"
365dfecf96Smrg#include "lisp/private.h"
375dfecf96Smrg#include <errno.h>
385dfecf96Smrg#include <fcntl.h>
395dfecf96Smrg#include <signal.h>
405dfecf96Smrg#include <string.h>
415dfecf96Smrg#include <sys/wait.h>
425dfecf96Smrg
435dfecf96Smrg/*
445dfecf96Smrg * Initialization
455dfecf96Smrg */
465dfecf96Smrg#define DIR_PROBE		0
475dfecf96Smrg#define DIR_INPUT		1
485dfecf96Smrg#define DIR_OUTPUT		2
495dfecf96Smrg#define DIR_IO			3
505dfecf96Smrg
515dfecf96Smrg#define EXT_NIL			0
525dfecf96Smrg#define EXT_ERROR		1
535dfecf96Smrg#define EXT_NEW_VERSION		2
545dfecf96Smrg#define EXT_RENAME		3
555dfecf96Smrg#define EXT_RENAME_DELETE	4
565dfecf96Smrg#define EXT_OVERWRITE		5
575dfecf96Smrg#define EXT_APPEND		6
585dfecf96Smrg#define EXT_SUPERSEDE		7
595dfecf96Smrg
605dfecf96Smrg#define NOEXT_NIL		0
615dfecf96Smrg#define NOEXT_ERROR		1
625dfecf96Smrg#define NOEXT_CREATE		2
635dfecf96Smrg#define NOEXT_NOTHING		3
645dfecf96Smrg
655dfecf96Smrgextern char **environ;
665dfecf96Smrg
675dfecf96SmrgLispObj *Oopen, *Oclose, *Otruename;
685dfecf96Smrg
695dfecf96SmrgLispObj *Kif_does_not_exist, *Kprobe, *Kinput, *Koutput, *Kio,
705dfecf96Smrg	*Knew_version, *Krename, *Krename_and_delete, *Koverwrite,
715dfecf96Smrg	*Kappend, *Ksupersede, *Kcreate;
725dfecf96Smrg
735dfecf96Smrg/*
745dfecf96Smrg * Implementation
755dfecf96Smrg */
765dfecf96Smrgvoid
775dfecf96SmrgLispStreamInit(void)
785dfecf96Smrg{
795dfecf96Smrg    Oopen		= STATIC_ATOM("OPEN");
805dfecf96Smrg    Oclose		= STATIC_ATOM("CLOSE");
815dfecf96Smrg    Otruename		= STATIC_ATOM("TRUENAME");
825dfecf96Smrg
835dfecf96Smrg    Kif_does_not_exist	= KEYWORD("IF-DOES-NOT-EXIST");
845dfecf96Smrg    Kprobe		= KEYWORD("PROBE");
855dfecf96Smrg    Kinput		= KEYWORD("INPUT");
865dfecf96Smrg    Koutput		= KEYWORD("OUTPUT");
875dfecf96Smrg    Kio			= KEYWORD("IO");
885dfecf96Smrg    Knew_version	= KEYWORD("NEW-VERSION");
895dfecf96Smrg    Krename		= KEYWORD("RENAME");
905dfecf96Smrg    Krename_and_delete	= KEYWORD("RENAME-AND-DELETE");
915dfecf96Smrg    Koverwrite		= KEYWORD("OVERWRITE");
925dfecf96Smrg    Kappend		= KEYWORD("APPEND");
935dfecf96Smrg    Ksupersede		= KEYWORD("SUPERSEDE");
945dfecf96Smrg    Kcreate		= KEYWORD("CREATE");
955dfecf96Smrg}
965dfecf96Smrg
975dfecf96SmrgLispObj *
985dfecf96SmrgLisp_DeleteFile(LispBuiltin *builtin)
995dfecf96Smrg/*
1005dfecf96Smrg delete-file filename
1015dfecf96Smrg */
1025dfecf96Smrg{
1035dfecf96Smrg    GC_ENTER();
1045dfecf96Smrg    LispObj *filename;
1055dfecf96Smrg
1065dfecf96Smrg    filename = ARGUMENT(0);
1075dfecf96Smrg
1085dfecf96Smrg    if (STRINGP(filename)) {
1095dfecf96Smrg	filename = APPLY1(Oparse_namestring, filename);
1105dfecf96Smrg	GC_PROTECT(filename);
1115dfecf96Smrg    }
1125dfecf96Smrg    else if (STREAMP(filename)) {
1135dfecf96Smrg	if (filename->data.stream.type != LispStreamFile)
1145dfecf96Smrg	    LispDestroy("%s: %s is not a FILE-STREAM",
1155dfecf96Smrg			STRFUN(builtin), STROBJ(filename));
1165dfecf96Smrg	filename = filename->data.stream.pathname;
1175dfecf96Smrg    }
1185dfecf96Smrg    else {
1195dfecf96Smrg	CHECK_PATHNAME(filename);
1205dfecf96Smrg    }
1215dfecf96Smrg    GC_LEAVE();
1225dfecf96Smrg
1235dfecf96Smrg    return (LispUnlink(THESTR(CAR(filename->data.pathname))) ? NIL : T);
1245dfecf96Smrg}
1255dfecf96Smrg
1265dfecf96SmrgLispObj *
1275dfecf96SmrgLisp_RenameFile(LispBuiltin *builtin)
1285dfecf96Smrg/*
1295dfecf96Smrg rename-file filename new-name
1305dfecf96Smrg */
1315dfecf96Smrg{
1325dfecf96Smrg    int code;
1335dfecf96Smrg    GC_ENTER();
1345dfecf96Smrg    char *from, *to;
1355dfecf96Smrg    LispObj *old_truename, *new_truename;
1365dfecf96Smrg
1375dfecf96Smrg    LispObj *filename, *new_name;
1385dfecf96Smrg
1395dfecf96Smrg    new_name = ARGUMENT(1);
1405dfecf96Smrg    filename = ARGUMENT(0);
1415dfecf96Smrg
1425dfecf96Smrg    if (STRINGP(filename)) {
1435dfecf96Smrg	filename = APPLY1(Oparse_namestring, filename);
1445dfecf96Smrg	GC_PROTECT(filename);
1455dfecf96Smrg    }
1465dfecf96Smrg    else if (STREAMP(filename)) {
1475dfecf96Smrg	if (filename->data.stream.type != LispStreamFile)
1485dfecf96Smrg	    LispDestroy("%s: %s is not a FILE-STREAM",
1495dfecf96Smrg			STRFUN(builtin), STROBJ(filename));
1505dfecf96Smrg	filename = filename->data.stream.pathname;
1515dfecf96Smrg    }
1525dfecf96Smrg    else {
1535dfecf96Smrg	CHECK_PATHNAME(filename);
1545dfecf96Smrg    }
1555dfecf96Smrg    old_truename = APPLY1(Otruename, filename);
1565dfecf96Smrg    GC_PROTECT(old_truename);
1575dfecf96Smrg
1585dfecf96Smrg    if (STRINGP(new_name)) {
1595dfecf96Smrg	new_name = APPLY3(Oparse_namestring, new_name, NIL, filename);
1605dfecf96Smrg	GC_PROTECT(new_name);
1615dfecf96Smrg    }
1625dfecf96Smrg    else {
1635dfecf96Smrg	CHECK_PATHNAME(new_name);
1645dfecf96Smrg    }
1655dfecf96Smrg
1665dfecf96Smrg    from = THESTR(CAR(filename->data.pathname));
1675dfecf96Smrg    to = THESTR(CAR(new_name->data.pathname));
1685dfecf96Smrg    code = LispRename(from, to);
1695dfecf96Smrg    if (code)
1705dfecf96Smrg	LispDestroy("%s: rename(%s, %s): %s",
1715dfecf96Smrg		    STRFUN(builtin), from, to, strerror(errno));
1725dfecf96Smrg    GC_LEAVE();
1735dfecf96Smrg
1745dfecf96Smrg    new_truename = APPLY1(Otruename, new_name);
1755dfecf96Smrg    RETURN_COUNT = 2;
1765dfecf96Smrg    RETURN(0) = old_truename;
1775dfecf96Smrg    RETURN(1) = new_truename;
1785dfecf96Smrg
1795dfecf96Smrg    return (new_name);
1805dfecf96Smrg}
1815dfecf96Smrg
1825dfecf96SmrgLispObj *
1835dfecf96SmrgLisp_Streamp(LispBuiltin *builtin)
1845dfecf96Smrg/*
1855dfecf96Smrg streamp object
1865dfecf96Smrg */
1875dfecf96Smrg{
1885dfecf96Smrg    LispObj *object;
1895dfecf96Smrg
1905dfecf96Smrg    object = ARGUMENT(0);
1915dfecf96Smrg
1925dfecf96Smrg    return (STREAMP(object) ? T : NIL);
1935dfecf96Smrg}
1945dfecf96Smrg
1955dfecf96SmrgLispObj *
1965dfecf96SmrgLisp_InputStreamP(LispBuiltin *builtin)
1975dfecf96Smrg/*
1985dfecf96Smrg input-stream-p stream
1995dfecf96Smrg */
2005dfecf96Smrg{
2015dfecf96Smrg    LispObj *stream;
2025dfecf96Smrg
2035dfecf96Smrg    stream = ARGUMENT(0);
2045dfecf96Smrg
2055dfecf96Smrg    CHECK_STREAM(stream);
2065dfecf96Smrg
2075dfecf96Smrg    return (stream->data.stream.readable ? T : NIL);
2085dfecf96Smrg}
2095dfecf96Smrg
2105dfecf96SmrgLispObj *
2115dfecf96SmrgLisp_OpenStreamP(LispBuiltin *builtin)
2125dfecf96Smrg/*
2135dfecf96Smrg open-stream-p stream
2145dfecf96Smrg */
2155dfecf96Smrg{
2165dfecf96Smrg   LispObj *stream;
2175dfecf96Smrg
2185dfecf96Smrg    stream = ARGUMENT(0);
2195dfecf96Smrg
2205dfecf96Smrg    CHECK_STREAM(stream);
2215dfecf96Smrg
2225dfecf96Smrg    return (stream->data.stream.readable || stream->data.stream.writable ?
2235dfecf96Smrg	    T : NIL);
2245dfecf96Smrg}
2255dfecf96Smrg
2265dfecf96SmrgLispObj *
2275dfecf96SmrgLisp_OutputStreamP(LispBuiltin *builtin)
2285dfecf96Smrg/*
2295dfecf96Smrg output-stream-p stream
2305dfecf96Smrg */
2315dfecf96Smrg{
2325dfecf96Smrg    LispObj *stream;
2335dfecf96Smrg
2345dfecf96Smrg    stream = ARGUMENT(0);
2355dfecf96Smrg
2365dfecf96Smrg    CHECK_STREAM(stream);
2375dfecf96Smrg
2385dfecf96Smrg    return (stream->data.stream.writable ? T : NIL);
2395dfecf96Smrg}
2405dfecf96Smrg
2415dfecf96SmrgLispObj *
2425dfecf96SmrgLisp_Open(LispBuiltin *builtin)
2435dfecf96Smrg/*
2445dfecf96Smrg open filename &key direction element-type if-exists if-does-not-exist external-format
2455dfecf96Smrg */
2465dfecf96Smrg{
2475dfecf96Smrg    GC_ENTER();
2485dfecf96Smrg    char *string;
2495dfecf96Smrg    LispObj *stream = NIL;
2505dfecf96Smrg    int mode, flags, direction, exist, noexist, file_exist;
2515dfecf96Smrg    LispFile *file;
2525dfecf96Smrg
2535dfecf96Smrg    LispObj *filename, *odirection, *element_type, *if_exists,
2545dfecf96Smrg	    *if_does_not_exist, *external_format;
2555dfecf96Smrg
2565dfecf96Smrg    external_format = ARGUMENT(5);
2575dfecf96Smrg    if_does_not_exist = ARGUMENT(4);
2585dfecf96Smrg    if_exists = ARGUMENT(3);
2595dfecf96Smrg    element_type = ARGUMENT(2);
2605dfecf96Smrg    odirection = ARGUMENT(1);
2615dfecf96Smrg    filename = ARGUMENT(0);
2625dfecf96Smrg
2635dfecf96Smrg    if (STRINGP(filename)) {
2645dfecf96Smrg	filename = APPLY1(Oparse_namestring, filename);
2655dfecf96Smrg	GC_PROTECT(filename);
2665dfecf96Smrg    }
2675dfecf96Smrg    else if (STREAMP(filename)) {
2685dfecf96Smrg	if (filename->data.stream.type != LispStreamFile)
2695dfecf96Smrg	    LispDestroy("%s: %s is not a FILE-STREAM",
2705dfecf96Smrg			STRFUN(builtin), STROBJ(filename));
2715dfecf96Smrg	filename = filename->data.stream.pathname;
2725dfecf96Smrg    }
2735dfecf96Smrg    else {
2745dfecf96Smrg	CHECK_PATHNAME(filename);
2755dfecf96Smrg    }
2765dfecf96Smrg
2775dfecf96Smrg    if (odirection != UNSPEC) {
2785dfecf96Smrg	direction = -1;
2795dfecf96Smrg	if (KEYWORDP(odirection)) {
2805dfecf96Smrg	    if (odirection == Kprobe)
2815dfecf96Smrg		direction = DIR_PROBE;
2825dfecf96Smrg	    else if (odirection == Kinput)
2835dfecf96Smrg		direction = DIR_INPUT;
2845dfecf96Smrg	    else if (odirection == Koutput)
2855dfecf96Smrg		direction = DIR_OUTPUT;
2865dfecf96Smrg	    else if (odirection == Kio)
2875dfecf96Smrg		direction = DIR_IO;
2885dfecf96Smrg	}
2895dfecf96Smrg	if (direction == -1)
2905dfecf96Smrg	    LispDestroy("%s: bad :DIRECTION %s",
2915dfecf96Smrg			STRFUN(builtin), STROBJ(odirection));
2925dfecf96Smrg    }
2935dfecf96Smrg    else
2945dfecf96Smrg	direction = DIR_INPUT;
2955dfecf96Smrg
2965dfecf96Smrg    if (element_type != UNSPEC) {
2975dfecf96Smrg	/* just check argument... */
2985dfecf96Smrg	if (SYMBOLP(element_type) &&
2995dfecf96Smrg	    ATOMID(element_type) == Scharacter)
3005dfecf96Smrg	    ;	/* do nothing */
3015dfecf96Smrg	else if (KEYWORDP(element_type) &&
3025dfecf96Smrg	    ATOMID(element_type) == Sdefault)
3035dfecf96Smrg	    ;	/* do nothing */
3045dfecf96Smrg	else
3055dfecf96Smrg	    LispDestroy("%s: only :%s and %s supported for :ELEMENT-TYPE, not %s",
306f14f4646Smrg			STRFUN(builtin), Sdefault->value, Scharacter->value, STROBJ(element_type));
3075dfecf96Smrg    }
3085dfecf96Smrg
3095dfecf96Smrg    if (if_exists != UNSPEC) {
3105dfecf96Smrg	exist = -1;
3115dfecf96Smrg	if (if_exists == NIL)
3125dfecf96Smrg	    exist = EXT_NIL;
3135dfecf96Smrg	else if (KEYWORDP(if_exists)) {
3145dfecf96Smrg	    if (if_exists == Kerror)
3155dfecf96Smrg		exist = EXT_ERROR;
3165dfecf96Smrg	    else if (if_exists == Knew_version)
3175dfecf96Smrg		exist = EXT_NEW_VERSION;
3185dfecf96Smrg	    else if (if_exists == Krename)
3195dfecf96Smrg		exist = EXT_RENAME;
3205dfecf96Smrg	    else if (if_exists == Krename_and_delete)
3215dfecf96Smrg		exist = EXT_RENAME_DELETE;
3225dfecf96Smrg	    else if (if_exists == Koverwrite)
3235dfecf96Smrg		exist = EXT_OVERWRITE;
3245dfecf96Smrg	    else if (if_exists == Kappend)
3255dfecf96Smrg		exist = EXT_APPEND;
3265dfecf96Smrg	    else if (if_exists == Ksupersede)
3275dfecf96Smrg		exist = EXT_SUPERSEDE;
3285dfecf96Smrg	}
3295dfecf96Smrg	if (exist == -1)
3305dfecf96Smrg	    LispDestroy("%s: bad :IF-EXISTS %s",
3315dfecf96Smrg			STRFUN(builtin), STROBJ(if_exists));
3325dfecf96Smrg    }
3335dfecf96Smrg    else
3345dfecf96Smrg	exist = EXT_ERROR;
3355dfecf96Smrg
3365dfecf96Smrg    if (if_does_not_exist != UNSPEC) {
3375dfecf96Smrg	noexist = -1;
3385dfecf96Smrg	if (if_does_not_exist == NIL)
3395dfecf96Smrg	    noexist = NOEXT_NIL;
3405dfecf96Smrg	if (KEYWORDP(if_does_not_exist)) {
3415dfecf96Smrg	    if (if_does_not_exist == Kerror)
3425dfecf96Smrg		noexist = NOEXT_ERROR;
3435dfecf96Smrg	    else if (if_does_not_exist == Kcreate)
3445dfecf96Smrg		noexist = NOEXT_CREATE;
3455dfecf96Smrg	}
3465dfecf96Smrg	if (noexist == -1)
3475dfecf96Smrg	    LispDestroy("%s: bad :IF-DOES-NO-EXISTS %s",
3485dfecf96Smrg			STRFUN(builtin), STROBJ(if_does_not_exist));
3495dfecf96Smrg    }
3505dfecf96Smrg    else
3515dfecf96Smrg	noexist = direction != DIR_INPUT ? NOEXT_NOTHING : NOEXT_ERROR;
3525dfecf96Smrg
3535dfecf96Smrg    if (external_format != UNSPEC) {
3545dfecf96Smrg	/* just check argument... */
3555dfecf96Smrg	if (SYMBOLP(external_format) &&
3565dfecf96Smrg	    ATOMID(external_format) == Scharacter)
3575dfecf96Smrg	    ;	/* do nothing */
3585dfecf96Smrg	else if (KEYWORDP(external_format) &&
3595dfecf96Smrg	    ATOMID(external_format) == Sdefault)
3605dfecf96Smrg	    ;	/* do nothing */
3615dfecf96Smrg	else
3625dfecf96Smrg	    LispDestroy("%s: only :%s and %s supported for :EXTERNAL-FORMAT, not %s",
363f14f4646Smrg			STRFUN(builtin), Sdefault->value, Scharacter->value, STROBJ(external_format));
3645dfecf96Smrg    }
3655dfecf96Smrg
3665dfecf96Smrg    /* string representation of pathname */
3675dfecf96Smrg    string = THESTR(CAR(filename->data.pathname));
3685dfecf96Smrg    mode = 0;
3695dfecf96Smrg
3705dfecf96Smrg    file_exist = access(string, F_OK) == 0;
3715dfecf96Smrg    if (file_exist) {
3725dfecf96Smrg	if (exist == EXT_NIL) {
3735dfecf96Smrg	    GC_LEAVE();
3745dfecf96Smrg	    return (NIL);
3755dfecf96Smrg	}
3765dfecf96Smrg    }
3775dfecf96Smrg    else {
3785dfecf96Smrg	if (noexist == NOEXT_NIL) {
3795dfecf96Smrg	    GC_LEAVE();
3805dfecf96Smrg	    return (NIL);
3815dfecf96Smrg	}
3825dfecf96Smrg	if (noexist == NOEXT_ERROR)
3835dfecf96Smrg	    LispDestroy("%s: file %s does not exist",
3845dfecf96Smrg			STRFUN(builtin), STROBJ(CAR(filename->data.quote)));
3855dfecf96Smrg	else if (noexist == NOEXT_CREATE) {
3865dfecf96Smrg	    LispFile *tmp = LispFopen(string, FILE_WRITE);
3875dfecf96Smrg
3885dfecf96Smrg	    if (tmp)
3895dfecf96Smrg		LispFclose(tmp);
3905dfecf96Smrg	    else
3915dfecf96Smrg		LispDestroy("%s: cannot create file %s",
3925dfecf96Smrg			    STRFUN(builtin),
3935dfecf96Smrg			    STROBJ(CAR(filename->data.quote)));
3945dfecf96Smrg	}
3955dfecf96Smrg    }
3965dfecf96Smrg
3975dfecf96Smrg    if (direction == DIR_OUTPUT || direction == DIR_IO) {
3985dfecf96Smrg	if (file_exist) {
3995dfecf96Smrg	    if (exist == EXT_ERROR)
4005dfecf96Smrg		LispDestroy("%s: file %s already exists",
4015dfecf96Smrg			    STRFUN(builtin), STROBJ(CAR(filename->data.quote)));
4025dfecf96Smrg	    if (exist == EXT_RENAME) {
4035dfecf96Smrg		/* Add an ending '~' at the end of the backup file */
4045dfecf96Smrg		char tmp[PATH_MAX + 1];
4055dfecf96Smrg
4065dfecf96Smrg		strcpy(tmp, string);
4075dfecf96Smrg		if (strlen(tmp) + 1 > PATH_MAX)
4085dfecf96Smrg		    LispDestroy("%s: backup name for %s too long",
4095dfecf96Smrg				STRFUN(builtin),
4105dfecf96Smrg				STROBJ(CAR(filename->data.quote)));
4115dfecf96Smrg		strcat(tmp, "~");
4125dfecf96Smrg		if (rename(string, tmp))
4135dfecf96Smrg		    LispDestroy("%s: rename: %s",
4145dfecf96Smrg				STRFUN(builtin), strerror(errno));
4155dfecf96Smrg		mode |= FILE_WRITE;
4165dfecf96Smrg	    }
4175dfecf96Smrg	    else if (exist == EXT_OVERWRITE)
4185dfecf96Smrg		mode |= FILE_WRITE;
4195dfecf96Smrg	    else if (exist == EXT_APPEND)
4205dfecf96Smrg		mode |= FILE_APPEND;
4215dfecf96Smrg	}
4225dfecf96Smrg	else
4235dfecf96Smrg	    mode |= FILE_WRITE;
4245dfecf96Smrg	if (direction == DIR_IO)
4255dfecf96Smrg	    mode |= FILE_IO;
4265dfecf96Smrg    }
4275dfecf96Smrg    else
4285dfecf96Smrg	mode |= FILE_READ;
4295dfecf96Smrg
4305dfecf96Smrg    file = LispFopen(string, mode);
4315dfecf96Smrg    if (file == NULL)
4325dfecf96Smrg	LispDestroy("%s: open: %s", STRFUN(builtin), strerror(errno));
4335dfecf96Smrg
4345dfecf96Smrg    flags = 0;
4355dfecf96Smrg    if (direction == DIR_PROBE) {
4365dfecf96Smrg	LispFclose(file);
4375dfecf96Smrg	file = NULL;
4385dfecf96Smrg    }
4395dfecf96Smrg    else {
4405dfecf96Smrg	if (direction == DIR_INPUT || direction == DIR_IO)
4415dfecf96Smrg	    flags |= STREAM_READ;
4425dfecf96Smrg	if (direction == DIR_OUTPUT || direction == DIR_IO)
4435dfecf96Smrg	    flags |= STREAM_WRITE;
4445dfecf96Smrg    }
4455dfecf96Smrg    stream = FILESTREAM(file, filename, flags);
4465dfecf96Smrg    GC_LEAVE();
4475dfecf96Smrg
4485dfecf96Smrg    return (stream);
4495dfecf96Smrg}
4505dfecf96Smrg
4515dfecf96SmrgLispObj *
4525dfecf96SmrgLisp_Close(LispBuiltin *builtin)
4535dfecf96Smrg/*
4545dfecf96Smrg close stream &key abort
4555dfecf96Smrg */
4565dfecf96Smrg{
4575dfecf96Smrg    LispObj *stream, *oabort;
4585dfecf96Smrg
4595dfecf96Smrg    oabort = ARGUMENT(1);
4605dfecf96Smrg    stream = ARGUMENT(0);
4615dfecf96Smrg
4625dfecf96Smrg    CHECK_STREAM(stream);
4635dfecf96Smrg
4645dfecf96Smrg    if (stream->data.stream.readable || stream->data.stream.writable) {
4655dfecf96Smrg	stream->data.stream.readable = stream->data.stream.writable = 0;
4665dfecf96Smrg	if (stream->data.stream.type == LispStreamFile) {
4675dfecf96Smrg	    LispFclose(stream->data.stream.source.file);
4685dfecf96Smrg	    stream->data.stream.source.file = NULL;
4695dfecf96Smrg	}
4705dfecf96Smrg	else if (stream->data.stream.type == LispStreamPipe) {
4715dfecf96Smrg	    if (IPSTREAMP(stream)) {
4725dfecf96Smrg		LispFclose(IPSTREAMP(stream));
4735dfecf96Smrg		IPSTREAMP(stream) = NULL;
4745dfecf96Smrg	    }
4755dfecf96Smrg	    if (OPSTREAMP(stream)) {
4765dfecf96Smrg		LispFclose(OPSTREAMP(stream));
4775dfecf96Smrg		OPSTREAMP(stream) = NULL;
4785dfecf96Smrg	    }
4795dfecf96Smrg	    if (EPSTREAMP(stream)) {
4805dfecf96Smrg		LispFclose(EPSTREAMP(stream));
4815dfecf96Smrg		EPSTREAMP(stream) = NULL;
4825dfecf96Smrg	    }
4835dfecf96Smrg	    if (PIDPSTREAMP(stream) > 0) {
4845dfecf96Smrg		kill(PIDPSTREAMP(stream),
4855dfecf96Smrg		     oabort == UNSPEC || oabort == NIL ? SIGTERM : SIGKILL);
4865dfecf96Smrg		waitpid(PIDPSTREAMP(stream), NULL, 0);
4875dfecf96Smrg	    }
4885dfecf96Smrg	}
4895dfecf96Smrg	return (T);
4905dfecf96Smrg    }
4915dfecf96Smrg
4925dfecf96Smrg    return (NIL);
4935dfecf96Smrg}
4945dfecf96Smrg
4955dfecf96SmrgLispObj *
4965dfecf96SmrgLisp_Listen(LispBuiltin *builtin)
4975dfecf96Smrg/*
4985dfecf96Smrg listen &optional input-stream
4995dfecf96Smrg */
5005dfecf96Smrg{
5015dfecf96Smrg    LispFile *file = NULL;
5025dfecf96Smrg    LispObj *result = NIL;
5035dfecf96Smrg
5045dfecf96Smrg    LispObj *stream;
5055dfecf96Smrg
5065dfecf96Smrg    stream = ARGUMENT(0);
5075dfecf96Smrg
5085dfecf96Smrg    if (stream == UNSPEC)
5095dfecf96Smrg	stream = NIL;
5105dfecf96Smrg    else if (stream != NIL) {
5115dfecf96Smrg	CHECK_STREAM(stream);
5125dfecf96Smrg    }
5135dfecf96Smrg    else
5145dfecf96Smrg	stream = lisp__data.standard_input;
5155dfecf96Smrg
5165dfecf96Smrg    if (stream->data.stream.readable) {
5175dfecf96Smrg	switch (stream->data.stream.type) {
5185dfecf96Smrg	    case LispStreamString:
5195dfecf96Smrg		if (SSTREAMP(stream)->input < SSTREAMP(stream)->length)
5205dfecf96Smrg		    result = T;
5215dfecf96Smrg		break;
5225dfecf96Smrg	    case LispStreamFile:
5235dfecf96Smrg		file = FSTREAMP(stream);
5245dfecf96Smrg		break;
5255dfecf96Smrg	    case LispStreamStandard:
5265dfecf96Smrg		file = FSTREAMP(stream);
5275dfecf96Smrg		break;
5285dfecf96Smrg	    case LispStreamPipe:
5295dfecf96Smrg		file = IPSTREAMP(stream);
5305dfecf96Smrg		break;
5315dfecf96Smrg	}
5325dfecf96Smrg
5335dfecf96Smrg	if (file != NULL) {
5345dfecf96Smrg	    if (file->available || file->offset < file->length)
5355dfecf96Smrg		result = T;
5365dfecf96Smrg	    else {
5375dfecf96Smrg		unsigned char c;
5385dfecf96Smrg
5395dfecf96Smrg		if (!file->nonblock) {
5405dfecf96Smrg		    if (fcntl(file->descriptor, F_SETFL, O_NONBLOCK) < 0)
5415dfecf96Smrg			LispDestroy("%s: fcntl: %s",
5425dfecf96Smrg				    STRFUN(builtin), strerror(errno));
5435dfecf96Smrg		    file->nonblock = 1;
5445dfecf96Smrg		}
5455dfecf96Smrg		if (read(file->descriptor, &c, 1) == 1) {
5465dfecf96Smrg		    LispFungetc(file, c);
5475dfecf96Smrg		    result = T;
5485dfecf96Smrg		}
5495dfecf96Smrg	    }
5505dfecf96Smrg	}
5515dfecf96Smrg    }
5525dfecf96Smrg
5535dfecf96Smrg    return (result);
5545dfecf96Smrg}
5555dfecf96Smrg
5565dfecf96SmrgLispObj *
5575dfecf96SmrgLisp_MakeStringInputStream(LispBuiltin *builtin)
5585dfecf96Smrg/*
5595dfecf96Smrg make-string-input-stream string &optional start end
5605dfecf96Smrg */
5615dfecf96Smrg{
5625dfecf96Smrg    char *string;
5635dfecf96Smrg    long start, end, length;
5645dfecf96Smrg
5655dfecf96Smrg    LispObj *ostring, *ostart, *oend, *result;
5665dfecf96Smrg
5675dfecf96Smrg    oend = ARGUMENT(2);
5685dfecf96Smrg    ostart = ARGUMENT(1);
5695dfecf96Smrg    ostring = ARGUMENT(0);
5705dfecf96Smrg
5715dfecf96Smrg    start = end = 0;
5725dfecf96Smrg    CHECK_STRING(ostring);
5735dfecf96Smrg    LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
5745dfecf96Smrg			      &start, &end, &length);
5755dfecf96Smrg    string = THESTR(ostring);
5765dfecf96Smrg
5775dfecf96Smrg    if (end - start != length)
5785dfecf96Smrg	length = end - start;
5795dfecf96Smrg    result = LSTRINGSTREAM(string + start, STREAM_READ, length);
5805dfecf96Smrg
5815dfecf96Smrg    return (result);
5825dfecf96Smrg}
5835dfecf96Smrg
5845dfecf96SmrgLispObj *
5855dfecf96SmrgLisp_MakeStringOutputStream(LispBuiltin *builtin)
5865dfecf96Smrg/*
5875dfecf96Smrg make-string-output-stream &key element-type
5885dfecf96Smrg */
5895dfecf96Smrg{
5905dfecf96Smrg    LispObj *element_type;
5915dfecf96Smrg
5925dfecf96Smrg    element_type = ARGUMENT(0);
5935dfecf96Smrg
5945dfecf96Smrg    if (element_type != UNSPEC) {
5955dfecf96Smrg	/* just check argument... */
5965dfecf96Smrg	if (SYMBOLP(element_type) && ATOMID(element_type) == Scharacter)
5975dfecf96Smrg	    ;	/* do nothing */
5985dfecf96Smrg	else if (KEYWORDP(element_type) &&
5995dfecf96Smrg	    ATOMID(element_type) == Sdefault)
6005dfecf96Smrg	    ;	/* do nothing */
6015dfecf96Smrg	else
6025dfecf96Smrg	    LispDestroy("%s: only :%s and %s supported for :ELEMENT-TYPE, not %s",
603f14f4646Smrg			STRFUN(builtin), Sdefault->value, Scharacter->value, STROBJ(element_type));
6045dfecf96Smrg    }
6055dfecf96Smrg
6065dfecf96Smrg    return (LSTRINGSTREAM("", STREAM_WRITE, 1));
6075dfecf96Smrg}
6085dfecf96Smrg
6095dfecf96SmrgLispObj *
6105dfecf96SmrgLisp_GetOutputStreamString(LispBuiltin *builtin)
6115dfecf96Smrg/*
6125dfecf96Smrg get-output-stream-string string-output-stream
6135dfecf96Smrg */
6145dfecf96Smrg{
6155dfecf96Smrg    int length;
616f765521fSmrg    const char *string;
6175dfecf96Smrg    LispObj *string_output_stream, *result;
6185dfecf96Smrg
6195dfecf96Smrg    string_output_stream = ARGUMENT(0);
6205dfecf96Smrg
6215dfecf96Smrg    if (!STREAMP(string_output_stream) ||
6225dfecf96Smrg	string_output_stream->data.stream.type != LispStreamString ||
6235dfecf96Smrg	string_output_stream->data.stream.readable ||
6245dfecf96Smrg	!string_output_stream->data.stream.writable)
6255dfecf96Smrg	LispDestroy("%s: %s is not an output string stream",
6265dfecf96Smrg		    STRFUN(builtin), STROBJ(string_output_stream));
6275dfecf96Smrg
6285dfecf96Smrg    string = LispGetSstring(SSTREAMP(string_output_stream), &length);
6295dfecf96Smrg    result = LSTRING(string, length);
6305dfecf96Smrg
6315dfecf96Smrg    /* reset string */
6325dfecf96Smrg    SSTREAMP(string_output_stream)->output =
6335dfecf96Smrg	SSTREAMP(string_output_stream)->length =
6345dfecf96Smrg	SSTREAMP(string_output_stream)->column = 0;
6355dfecf96Smrg
6365dfecf96Smrg    return (result);
6375dfecf96Smrg}
6385dfecf96Smrg
6395dfecf96Smrg
6405dfecf96Smrg/* XXX Non standard functions below
6415dfecf96Smrg */
6425dfecf96SmrgLispObj *
6435dfecf96SmrgLisp_MakePipe(LispBuiltin *builtin)
6445dfecf96Smrg/*
6455dfecf96Smrg make-pipe command-line &key :direction :element-type :external-format
6465dfecf96Smrg */
6475dfecf96Smrg{
6485dfecf96Smrg    char *string;
6495dfecf96Smrg    LispObj *stream = NIL;
6505dfecf96Smrg    int flags, direction;
6515dfecf96Smrg    LispFile *error_file;
6525dfecf96Smrg    LispPipe *program;
6535dfecf96Smrg    int ifd[2];
6545dfecf96Smrg    int ofd[2];
6555dfecf96Smrg    int efd[2];
6565dfecf96Smrg    char *argv[4];
6575dfecf96Smrg
6585dfecf96Smrg    LispObj *command_line, *odirection, *element_type, *external_format;
6595dfecf96Smrg
6605dfecf96Smrg    external_format = ARGUMENT(3);
6615dfecf96Smrg    element_type = ARGUMENT(2);
6625dfecf96Smrg    odirection = ARGUMENT(1);
6635dfecf96Smrg    command_line = ARGUMENT(0);
6645dfecf96Smrg
6655dfecf96Smrg    if (PATHNAMEP(command_line))
6665dfecf96Smrg	command_line = CAR(command_line->data.quote);
6675dfecf96Smrg    else if (!STRINGP(command_line))
6685dfecf96Smrg	LispDestroy("%s: %s is a bad pathname",
6695dfecf96Smrg		    STRFUN(builtin), STROBJ(command_line));
6705dfecf96Smrg
6715dfecf96Smrg    if (odirection != UNSPEC) {
6725dfecf96Smrg	direction = -1;
6735dfecf96Smrg	if (KEYWORDP(odirection)) {
6745dfecf96Smrg	    if (odirection == Kprobe)
6755dfecf96Smrg		direction = DIR_PROBE;
6765dfecf96Smrg	    else if (odirection == Kinput)
6775dfecf96Smrg		direction = DIR_INPUT;
6785dfecf96Smrg	    else if (odirection == Koutput)
6795dfecf96Smrg		direction = DIR_OUTPUT;
6805dfecf96Smrg	    else if (odirection == Kio)
6815dfecf96Smrg		direction = DIR_IO;
6825dfecf96Smrg	}
6835dfecf96Smrg	if (direction == -1)
6845dfecf96Smrg	    LispDestroy("%s: bad :DIRECTION %s",
6855dfecf96Smrg			STRFUN(builtin), STROBJ(odirection));
6865dfecf96Smrg    }
6875dfecf96Smrg    else
6885dfecf96Smrg	direction = DIR_INPUT;
6895dfecf96Smrg
6905dfecf96Smrg    if (element_type != UNSPEC) {
6915dfecf96Smrg	/* just check argument... */
6925dfecf96Smrg	if (SYMBOLP(element_type) && ATOMID(element_type) == Scharacter)
6935dfecf96Smrg	    ;	/* do nothing */
6945dfecf96Smrg	else if (KEYWORDP(element_type) && ATOMID(element_type) == Sdefault)
6955dfecf96Smrg	    ;	/* do nothing */
6965dfecf96Smrg	else
6975dfecf96Smrg	    LispDestroy("%s: only :%s and %s supported for :ELEMENT-TYPE, not %s",
698f14f4646Smrg			STRFUN(builtin), Sdefault->value, Scharacter->value, STROBJ(element_type));
6995dfecf96Smrg    }
7005dfecf96Smrg
7015dfecf96Smrg    if (external_format != UNSPEC) {
7025dfecf96Smrg	/* just check argument... */
7035dfecf96Smrg	if (SYMBOLP(external_format) && ATOMID(external_format) == Scharacter)
7045dfecf96Smrg	    ;	/* do nothing */
7055dfecf96Smrg	else if (KEYWORDP(external_format) &&
7065dfecf96Smrg		 ATOMID(external_format) == Sdefault)
7075dfecf96Smrg	    ;	/* do nothing */
7085dfecf96Smrg	else
7095dfecf96Smrg	    LispDestroy("%s: only :%s and %s supported for :EXTERNAL-FORMAT, not %s",
710f14f4646Smrg			STRFUN(builtin), Sdefault->value, Scharacter->value, STROBJ(external_format));
7115dfecf96Smrg    }
7125dfecf96Smrg
7135dfecf96Smrg    string = THESTR(command_line);
7145dfecf96Smrg    program = LispMalloc(sizeof(LispPipe));
7155dfecf96Smrg    if (direction != DIR_PROBE) {
7165dfecf96Smrg	argv[0] = "sh";
7175dfecf96Smrg	argv[1] = "-c";
7185dfecf96Smrg	argv[2] = string;
7195dfecf96Smrg	argv[3] = NULL;
7205dfecf96Smrg	pipe(ifd);
7215dfecf96Smrg	pipe(ofd);
7225dfecf96Smrg	pipe(efd);
7235dfecf96Smrg	if ((program->pid = fork()) == 0) {
7245dfecf96Smrg	    close(0);
7255dfecf96Smrg	    close(1);
7265dfecf96Smrg	    close(2);
7275dfecf96Smrg	    dup2(ofd[0], 0);
7285dfecf96Smrg	    dup2(ifd[1], 1);
7295dfecf96Smrg	    dup2(efd[1], 2);
7305dfecf96Smrg	    close(ifd[0]);
7315dfecf96Smrg	    close(ifd[1]);
7325dfecf96Smrg	    close(ofd[0]);
7335dfecf96Smrg	    close(ofd[1]);
7345dfecf96Smrg	    close(efd[0]);
7355dfecf96Smrg	    close(efd[1]);
7365dfecf96Smrg	    execve("/bin/sh", argv, environ);
7375dfecf96Smrg	    exit(-1);
7385dfecf96Smrg	}
7395dfecf96Smrg	else if (program->pid < 0)
7405dfecf96Smrg	    LispDestroy("%s: fork: %s", STRFUN(builtin), strerror(errno));
7415dfecf96Smrg
7425dfecf96Smrg	program->input = LispFdopen(ifd[0], FILE_READ | FILE_UNBUFFERED);
7435dfecf96Smrg	close(ifd[1]);
7445dfecf96Smrg	program->output = LispFdopen(ofd[1], FILE_WRITE | FILE_UNBUFFERED);
7455dfecf96Smrg	close(ofd[0]);
7465dfecf96Smrg	error_file = LispFdopen(efd[0], FILE_READ | FILE_UNBUFFERED);
7475dfecf96Smrg	close(efd[1]);
7485dfecf96Smrg    }
7495dfecf96Smrg    else {
7505dfecf96Smrg	program->pid = -1;
7515dfecf96Smrg	program->input = program->output = error_file = NULL;
7525dfecf96Smrg    }
7535dfecf96Smrg
7545dfecf96Smrg    flags = direction == DIR_PROBE ? 0 : STREAM_READ;
7555dfecf96Smrg    program->errorp = FILESTREAM(error_file, command_line, flags);
7565dfecf96Smrg
7575dfecf96Smrg    flags = 0;
7585dfecf96Smrg    if (direction != DIR_PROBE) {
7595dfecf96Smrg	if (direction == DIR_INPUT || direction == DIR_IO)
7605dfecf96Smrg	    flags |= STREAM_READ;
7615dfecf96Smrg	if (direction == DIR_OUTPUT || direction == DIR_IO)
7625dfecf96Smrg	    flags |= STREAM_WRITE;
7635dfecf96Smrg    }
7645dfecf96Smrg    stream = PIPESTREAM(program, command_line, flags);
7655dfecf96Smrg    LispMused(program);
7665dfecf96Smrg
7675dfecf96Smrg    return (stream);
7685dfecf96Smrg}
7695dfecf96Smrg
7705dfecf96Smrg/* Helper function, primarily for use with the xt module
7715dfecf96Smrg */
7725dfecf96SmrgLispObj *
7735dfecf96SmrgLisp_PipeBroken(LispBuiltin *builtin)
7745dfecf96Smrg/*
7755dfecf96Smrg pipe-broken pipe-stream
7765dfecf96Smrg */
7775dfecf96Smrg{
7785dfecf96Smrg    int pid, status, retval;
7795dfecf96Smrg    LispObj *result = NIL;
7805dfecf96Smrg
7815dfecf96Smrg    LispObj *pipe_stream;
7825dfecf96Smrg
7835dfecf96Smrg    pipe_stream = ARGUMENT(0);
7845dfecf96Smrg
7855dfecf96Smrg    if (!STREAMP(pipe_stream) ||
7865dfecf96Smrg	pipe_stream->data.stream.type != LispStreamPipe)
7875dfecf96Smrg	LispDestroy("%s: %s is not a pipe stream",
7885dfecf96Smrg		    STRFUN(builtin), STROBJ(pipe_stream));
7895dfecf96Smrg
7905dfecf96Smrg    if ((pid = PIDPSTREAMP(pipe_stream)) > 0) {
7915dfecf96Smrg	retval = waitpid(pid, &status, WNOHANG | WUNTRACED);
7925dfecf96Smrg	if (retval == pid || (retval == -1 && errno == ECHILD))
7935dfecf96Smrg	    result = T;
7945dfecf96Smrg    }
7955dfecf96Smrg
7965dfecf96Smrg    return (result);
7975dfecf96Smrg}
7985dfecf96Smrg
7995dfecf96Smrg/*
8005dfecf96Smrg Helper function, so that it is not required to redirect error output
8015dfecf96Smrg */
8025dfecf96SmrgLispObj *
8035dfecf96SmrgLisp_PipeErrorStream(LispBuiltin *builtin)
8045dfecf96Smrg/*
8055dfecf96Smrg pipe-error-stream pipe-stream
8065dfecf96Smrg */
8075dfecf96Smrg{
8085dfecf96Smrg    LispObj *pipe_stream;
8095dfecf96Smrg
8105dfecf96Smrg    pipe_stream = ARGUMENT(0);
8115dfecf96Smrg
8125dfecf96Smrg    if (!STREAMP(pipe_stream) ||
8135dfecf96Smrg	pipe_stream->data.stream.type != LispStreamPipe)
8145dfecf96Smrg	LispDestroy("%s: %s is not a pipe stream",
8155dfecf96Smrg		    STRFUN(builtin), STROBJ(pipe_stream));
8165dfecf96Smrg
8175dfecf96Smrg    return (pipe_stream->data.stream.source.program->errorp);
8185dfecf96Smrg}
8195dfecf96Smrg
8205dfecf96Smrg/*
8215dfecf96Smrg Helper function, primarily for use with the xt module
8225dfecf96Smrg */
8235dfecf96SmrgLispObj *
8245dfecf96SmrgLisp_PipeInputDescriptor(LispBuiltin *builtin)
8255dfecf96Smrg/*
8265dfecf96Smrg pipe-input-descriptor pipe-stream
8275dfecf96Smrg */
8285dfecf96Smrg{
8295dfecf96Smrg    LispObj *pipe_stream;
8305dfecf96Smrg
8315dfecf96Smrg    pipe_stream = ARGUMENT(0);
8325dfecf96Smrg
8335dfecf96Smrg    if (!STREAMP(pipe_stream) ||
8345dfecf96Smrg	pipe_stream->data.stream.type != LispStreamPipe)
8355dfecf96Smrg	LispDestroy("%s: %s is not a pipe stream",
8365dfecf96Smrg		    STRFUN(builtin), STROBJ(pipe_stream));
8375dfecf96Smrg    if (!IPSTREAMP(pipe_stream))
8385dfecf96Smrg	LispDestroy("%s: pipe %s is unreadable",
8395dfecf96Smrg		    STRFUN(builtin), STROBJ(pipe_stream));
8405dfecf96Smrg
8415dfecf96Smrg    return (INTEGER(LispFileno(IPSTREAMP(pipe_stream))));
8425dfecf96Smrg}
8435dfecf96Smrg
8445dfecf96Smrg/*
8455dfecf96Smrg Helper function, primarily for use with the xt module
8465dfecf96Smrg */
8475dfecf96SmrgLispObj *
8485dfecf96SmrgLisp_PipeErrorDescriptor(LispBuiltin *builtin)
8495dfecf96Smrg/*
8505dfecf96Smrg pipe-error-descriptor pipe-stream
8515dfecf96Smrg */
8525dfecf96Smrg{
8535dfecf96Smrg    LispObj *pipe_stream;
8545dfecf96Smrg
8555dfecf96Smrg    pipe_stream = ARGUMENT(0);
8565dfecf96Smrg
8575dfecf96Smrg    if (!STREAMP(pipe_stream) ||
8585dfecf96Smrg	pipe_stream->data.stream.type != LispStreamPipe)
8595dfecf96Smrg	LispDestroy("%s: %s is not a pipe stream",
8605dfecf96Smrg		    STRFUN(builtin), STROBJ(pipe_stream));
8615dfecf96Smrg    if (!EPSTREAMP(pipe_stream))
8625dfecf96Smrg	LispDestroy("%s: pipe %s is closed",
8635dfecf96Smrg		    STRFUN(builtin), STROBJ(pipe_stream));
8645dfecf96Smrg
8655dfecf96Smrg    return (INTEGER(LispFileno(EPSTREAMP(pipe_stream))));
8665dfecf96Smrg}
867