summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorleftmike <mikemon@gmail.com>2020-02-02 08:23:11 -0800
committerleftmike <mikemon@gmail.com>2020-03-07 11:04:16 -0800
commitebcf4cab52e244992356167a97ab27d0191ab190 (patch)
treee2ae7bd2b0c765e7ceb45211967d3838b620133f
parent1ed918c42da20276bfde13c83b3be30203b054da (diff)
add subprocess, inspired by the Racket APIprocesses
-rw-r--r--README.md7
-rw-r--r--src/base.scm134
-rw-r--r--src/charset.cpp4
-rw-r--r--src/foment.cpp10
-rw-r--r--src/foment.hpp28
-rw-r--r--src/io.cpp240
-rw-r--r--src/io.hpp14
-rw-r--r--src/main.cpp12
-rw-r--r--src/process.cpp558
-rw-r--r--src/syncthrd.cpp4
-rw-r--r--test/exitcode.cpp14
-rw-r--r--test/hang.cpp15
-rw-r--r--test/process.scm311
-rw-r--r--test/stdread.cpp30
-rw-r--r--test/stdwrite.cpp30
-rw-r--r--unix/makefile31
-rw-r--r--windows/makefile45
17 files changed, 1387 insertions, 100 deletions
diff --git a/README.md b/README.md
index d6ef73c..e6cdfb1 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
* Full R7RS.
* Libraries and programs work.
* Native threads and some synchronization primitives.
+* [Proccess](Processes) is a subset of the [Racket](https://racket-lang.org/)
+[Processes API](https://docs.racket-lang.org/reference/subprocess.html).
* Memory management featuring guardians and trackers. Guardians protect objects from being collected and trackers follow objects as they get moved by the copying part of the collector.
* Full Unicode including reading and writing unicode characters to the console. Files in UTF-8 and UTF-16 encoding can be read and written.
* The system is built around a compiler and VM. There is support for prompts and continuation marks.
@@ -25,11 +27,6 @@
See [Foment](https://github.com/leftmike/foment/wiki/Foment) for more details.
-Future plans include
-* Providing line numbers and stack traces on errors.
-* R7RS large SRFIs.
-* composable continuations
-
Please note that this is very much a work in progress. Please let me know if
you find bugs and omissions. I will do my best to fix them.
diff --git a/src/base.scm b/src/base.scm
index 55f421d..afe5575 100644
--- a/src/base.scm
+++ b/src/base.scm
@@ -596,6 +596,20 @@
lookup-type-tags
comparator-context
comparator-context-set!
+ subprocess
+ subprocess-wait
+ subprocess-status
+ subprocess-kill
+ subprocess-pid
+ subprocess?
+ system
+ system*
+ system/exit-code
+ system*/exit-code
+ process
+ process*
+ process/ports
+ process*/ports
current-milliseconds
time-apply
time
@@ -944,6 +958,126 @@
(lambda () expr1 expr2 ...)
(lambda () (leave-exclusive exclusive))))))
+ (define (subprocess stdout stdin stderr command . args)
+ (apply values (apply %subprocess #f stdout stdin stderr command args)))
+
+ (define (shell-cmd/args cmd)
+ (cond-expand
+ (windows (list (get-environment-variable "ComSpec") (string-append "/c " cmd)))
+ (else (list "/bin/sh" "-c" cmd))))
+
+ (define (system cmd)
+ (apply system* (shell-cmd/args cmd)))
+
+ (define (system* cmd . args)
+ (= (apply system*/exit-code cmd args) 0))
+
+ (define (system/exit-code cmd)
+ (apply system*/exit-code (shell-cmd/args cmd)))
+
+ (define (system*/exit-code cmd . args)
+ (let* ((lst (apply process*/ports (current-output-port) (current-input-port)
+ (current-error-port) cmd args))
+ (ctrl (cadddr (cdr lst))))
+ (ctrl 'wait)
+ (ctrl 'exit-code)))
+
+ (define (process cmd)
+ (apply process*/ports (current-output-port) (current-input-port) (current-error-port)
+ (shell-cmd/args cmd)))
+
+ (define (process* cmd . args)
+ (apply process*/ports (current-output-port) (current-input-port) (current-error-port)
+ cmd args))
+
+ (define (process/ports out in err cmd)
+ (apply process*/ports out in err (shell-cmd/args cmd)))
+
+ (define (process-pipes out chldin in chldout err chlderr)
+ (if (or
+ (and (output-port? out) (input-port? chldin))
+ (and (input-port? in) (output-port? chldout))
+ (and (output-port? err) (input-port? chlderr)))
+ (let ((lock (make-exclusive))
+ (done (make-condition))
+ (cnt 0))
+ (if (and (output-port? out) (input-port? chldin))
+ (run-thread
+ (lambda ()
+ (with-exclusive lock (set! cnt (+ cnt 1)))
+ (%copy-port chldin out)
+ (close-port chldin)
+ (with-exclusive lock
+ (set! cnt (- cnt 1))
+ (if (= cnt 0)
+ (condition-wake done))))))
+ (if (and (input-port? in) (output-port? chldout))
+ (run-thread
+ (lambda ()
+ (with-exclusive lock (set! cnt (+ cnt 1)))
+ (%copy-port in chldout)
+ (close-port chldout)
+ (with-exclusive lock
+ (set! cnt (- cnt 1))
+ (if (= cnt 0)
+ (condition-wake done))))))
+ (if (and (output-port? err) (input-port? chlderr))
+ (run-thread
+ (lambda ()
+ (with-exclusive lock (set! cnt (+ cnt 1)))
+ (%copy-port chlderr err)
+ (close-port chlderr)
+ (with-exclusive lock
+ (set! cnt (- cnt 1))
+ (if (= cnt 0)
+ (condition-wake done))))))
+ (lambda ()
+ (define (wait)
+ (if (= cnt 0)
+ (leave-exclusive lock)
+ (begin
+ (condition-wait done lock)
+ (wait))))
+ (enter-exclusive lock)
+ (wait)))
+ (lambda () #t)))
+
+ (define (process*/ports out in err cmd . args)
+ (let* ((ret (apply %subprocess #t out in err cmd args))
+ (sub (car ret))
+ (chldout (cadr ret))
+ (chldin (caddr ret))
+ (chlderr (cadddr ret))
+ (pipes-wait (process-pipes out chldout in chldin err chlderr)))
+ (list
+ (if out #f chldout)
+ (if in #f chldin)
+ (subprocess-pid sub)
+ (if err #f chlderr)
+ (lambda (what)
+ (case what
+ ((status)
+ (let ((ret (subprocess-status sub)))
+ (if (integer? ret)
+ (if (zero? ret)
+ 'done-ok
+ 'done-error)
+ ret)))
+ ((exit-code)
+ (let ((ret (subprocess-status sub)))
+ (and (integer? ret) ret)))
+ ((wait)
+ (subprocess-wait sub)
+ (pipes-wait))
+ ((interrupt)
+ (subprocess-kill sub #f))
+ ((kill)
+ (subprocess-kill sub #t))
+ (else
+ (full-error 'assertion-violation '<control-process> #f
+ "<control-process>: expected status, exit-code, wait, interrupt, or kill"
+ what)))))))
+
(define push-parameter (cons #f #f))
(define pop-parameter (cons #f #f))
diff --git a/src/charset.cpp b/src/charset.cpp
index 36079fa..746e46a 100644
--- a/src/charset.cpp
+++ b/src/charset.cpp
@@ -495,8 +495,8 @@ Define("ucs-range->char-set", UCSRangeToCharSetPrimitive)(long_t argc, FObject a
FixnumArgCheck("ucs-range->char-set", argv[0]);
FixnumArgCheck("ucs-range->char-set", argv[1]);
- FCh lower = AsFixnum(argv[0]);
- FCh upper = AsFixnum(argv[1]);
+ FCh lower = (FCh) AsFixnum(argv[0]);
+ FCh upper = (FCh) AsFixnum(argv[1]);
FObject base = EmptyCharSet;
if (argc > 2)
diff --git a/src/foment.cpp b/src/foment.cpp
index b0a1428..8847d0c 100644
--- a/src/foment.cpp
+++ b/src/foment.cpp
@@ -99,7 +99,7 @@ void ErrorExitFoment(const char * what, const char * msg)
{
if (CheckHeapFlag || VerboseFlag)
fprintf(stderr, "RandomSeed: " ULONG_FMT "\n", RandomSeed);
- ExitFoment();
+ FlushStandardPorts();
}
#ifdef FOMENT_UNIX
@@ -1163,6 +1163,8 @@ FObject ThreadPPrimitiveFn(long_t argc, FObject argv[]);
FObject ExclusivePPrimitiveFn(long_t argc, FObject argv[]);
FObject ConditionPPrimitiveFn(long_t argc, FObject argv[]);
FObject EphemeronPPrimitiveFn(long_t argc, FObject argv[]);
+FObject CharSetPPrimitiveFn(long_t argc, FObject argv[]);
+FObject SubprocessPPrimitiveFn(long_t argc, FObject argv[]);
static FObject LookupTypeTags(FObject ttp)
{
@@ -1208,6 +1210,10 @@ static FObject LookupTypeTags(FObject ttp)
return(List(MakeFixnum(ConditionTag + INDIRECT_TAG_OFFSET)));
else if (AsPrimitive(ttp)->PrimitiveFn == EphemeronPPrimitiveFn)
return(List(MakeFixnum(EphemeronTag + INDIRECT_TAG_OFFSET)));
+ else if (AsPrimitive(ttp)->PrimitiveFn == CharSetPPrimitiveFn)
+ return(List(MakeFixnum(CharSetTag + INDIRECT_TAG_OFFSET)));
+ else if (AsPrimitive(ttp)->PrimitiveFn == SubprocessPPrimitiveFn)
+ return(List(MakeFixnum(SubprocessTag + INDIRECT_TAG_OFFSET)));
}
return(EmptyListObject);
@@ -1406,6 +1412,7 @@ FIndirectType IndirectTypes[] =
{"builtin-type", WriteBuiltinType},
{"builtin", WriteBuiltin},
{"char-set", WriteCharSet},
+ {"subprocess", WriteSubprocess},
{"free", 0}
};
@@ -1511,6 +1518,7 @@ long_t SetupFoment(FThreadState * ts)
SetupStrings();
SetupVectors();
SetupCharSets();
+ SetupProcess();
SetupIO();
SetupFileSys();
SetupCompile();
diff --git a/src/foment.hpp b/src/foment.hpp
index be0ada4..fdcc8a0 100644
--- a/src/foment.hpp
+++ b/src/foment.hpp
@@ -225,6 +225,7 @@ typedef enum
BuiltinTypeTag,
BuiltinTag,
CharSetTag,
+ SubprocessTag,
FreeTag, // Only on Adult Generation
BadDogTag // Invalid Tag
} FIndirectTag;
@@ -543,11 +544,22 @@ typedef enum
FromEnd
} FPositionFrom;
+#ifdef FOMENT_WINDOWS
+typedef void * FFileHandle;
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+typedef long_t FFileHandle;
+#endif // FOMENT_UNIX
+
+#define INVALID_FILE_HANDLE ((FFileHandle) -1)
+
typedef void (*FCloseInputFn)(FObject port);
typedef void (*FCloseOutputFn)(FObject port);
typedef void (*FFlushOutputFn)(FObject port);
typedef int64_t (*FGetPositionFn)(FObject port);
typedef void (*FSetPositionFn)(FObject port, int64_t pos, FPositionFrom frm);
+typedef FFileHandle (*FGetFileHandleFn)(FObject port);
typedef struct
{
@@ -560,6 +572,7 @@ typedef struct
FFlushOutputFn FlushOutputFn;
FGetPositionFn GetPositionFn;
FSetPositionFn SetPositionFn;
+ FGetFileHandleFn GetFileHandleFn;
} FGenericPort;
#define AsGenericPort(obj) ((FGenericPort *) obj)
@@ -648,6 +661,8 @@ inline long_t PositioningPortP(FObject obj)
return(AsGenericPort(obj)->Flags & PORT_FLAG_POSITIONING);
}
+void FlushStandardPorts();
+
// Binary and textual ports
FObject HandOffPort(FObject port);
@@ -656,6 +671,7 @@ void CloseOutput(FObject port);
void FlushOutput(FObject port);
int64_t GetPosition(FObject port);
void SetPosition(FObject port, int64_t pos, FPositionFrom frm);
+FFileHandle GetFileHandle(FObject port);
// Binary ports
@@ -730,6 +746,10 @@ void WriteSimple(FObject port, FObject obj, long_t df);
void WritePortObject(FWriteContext * wctx, FObject obj);
+// ---- Subprocesses ----
+
+void WriteSubprocess(FWriteContext * wctx, FObject obj);
+
// ---- Pairs ----
#define PairP(obj) (IndirectTag(obj) == PairTag)
@@ -1598,6 +1618,12 @@ inline void AtLeastFourArgsCheck(const char * who, long_t argc)
RaiseExceptionC(Assertion, who, "expected at least four arguments", EmptyListObject);
}
+inline void AtLeastFiveArgsCheck(const char * who, long_t argc)
+{
+ if (argc < 5)
+ RaiseExceptionC(Assertion, who, "expected at least five arguments", EmptyListObject);
+}
+
inline void ZeroOrOneArgsCheck(const char * who, long_t argc)
{
if (argc > 1)
@@ -1923,7 +1949,6 @@ FObject ExecuteProc(FObject op);
long_t SetupFoment(FThreadState * ts);
extern ulong_t SetupComplete;
-void ExitFoment();
void ErrorExitFoment(const char * what, const char * msg);
// ---- Do Not Call Directly ----
@@ -1935,6 +1960,7 @@ void SetupCharacters();
void SetupStrings();
void SetupVectors();
void SetupCharSets();
+void SetupProcess();
void SetupHashTables();
void SetupCompare();
void SetupIO();
diff --git a/src/io.cpp b/src/io.cpp
index 3252683..b691328 100644
--- a/src/io.cpp
+++ b/src/io.cpp
@@ -66,9 +66,22 @@ FObject StandardError = NoValueObject;
// ---- Binary Ports ----
+static FFileHandle InvalidFileHandle(FObject port)
+{
+ return(INVALID_FILE_HANDLE);
+}
+
+static FFileHandle GetObjectFileHandle(FObject port)
+{
+ FAssert(BinaryPortP(port) || TextualPortP(port));
+
+ return(GetFileHandle(AsGenericPort(port)->Object));
+}
+
FObject MakeBinaryPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn,
FCloseOutputFn cofn, FFlushOutputFn fofn, FReadBytesFn rbfn, FByteReadyPFn brpfn,
- FWriteBytesFn wbfn, FGetPositionFn gpfn, FSetPositionFn spfn)
+ FWriteBytesFn wbfn, FGetPositionFn gpfn, FSetPositionFn spfn,
+ FGetFileHandleFn gfhfn, ulong_t flgs)
{
FAssert((cifn == 0 && rbfn == 0 && brpfn == 0) || (cifn != 0 && rbfn != 0 && brpfn != 0));
FAssert((cofn == 0 && wbfn == 0 && fofn == 0) || (cofn != 0 && wbfn != 0 && fofn != 0));
@@ -79,7 +92,8 @@ FObject MakeBinaryPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn,
"%make-binary-port");
port->Generic.Flags = (cifn != 0 ? (PORT_FLAG_INPUT | PORT_FLAG_INPUT_OPEN) : 0)
| (cofn != 0 ? (PORT_FLAG_OUTPUT | PORT_FLAG_OUTPUT_OPEN) : 0)
- | (gpfn != 0 ? PORT_FLAG_POSITIONING : 0);
+ | (gpfn != 0 ? PORT_FLAG_POSITIONING : 0)
+ | flgs;
port->Generic.Name = nam;
port->Generic.Object = obj;
port->Generic.Context = ctx;
@@ -88,6 +102,7 @@ FObject MakeBinaryPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn,
port->Generic.FlushOutputFn = fofn;
port->Generic.GetPositionFn = gpfn;
port->Generic.SetPositionFn = spfn;
+ port->Generic.GetFileHandleFn = gfhfn == 0 ? InvalidFileHandle : gfhfn;
port->ReadBytesFn = rbfn;
port->ByteReadyPFn = brpfn;
port->WriteBytesFn = wbfn;
@@ -403,7 +418,7 @@ static FObject MakeBufferedPort(FObject port)
}
port = HandOffPort(port);
- FObject nport = MakeBinaryPort(AsGenericPort(port)->Name, port, bc,
+ return(MakeBinaryPort(AsGenericPort(port)->Name, port, bc,
InputPortOpenP(port) ? BufferedCloseInput : 0,
OutputPortOpenP(port) ? BufferedCloseOutput : 0,
OutputPortOpenP(port) ? BufferedFlushOutput : 0,
@@ -411,10 +426,9 @@ static FObject MakeBufferedPort(FObject port)
InputPortOpenP(port) ? BufferedByteReadyP : 0,
OutputPortOpenP(port) ? BufferedWriteBytes : 0,
PositioningPortP(port) ? BufferedGetPosition : 0,
- PositioningPortP(port) ? BufferedSetPosition : 0);
- AsGenericPort(nport)->Flags |= PORT_FLAG_BUFFERED;
-
- return(nport);
+ PositioningPortP(port) ? BufferedSetPosition : 0,
+ GetObjectFileHandle,
+ PORT_FLAG_BUFFERED));
}
static void SocketCloseInput(FObject port)
@@ -494,12 +508,9 @@ static void SocketWriteBytes(FObject port, void * b, ulong_t bl)
static FObject MakeSocketPort(SOCKET s)
{
- FObject port = MakeBinaryPort(NoValueObject, NoValueObject, (void *) s, SocketCloseInput,
+ return(MakeBinaryPort(NoValueObject, NoValueObject, (void *) s, SocketCloseInput,
SocketCloseOutput, SocketFlushOutput, SocketReadBytes, SocketByteReadyP,
- SocketWriteBytes, 0, 0);
- AsGenericPort(port)->Flags |= PORT_FLAG_SOCKET;
-
- return(port);
+ SocketWriteBytes, 0, 0, 0, PORT_FLAG_SOCKET));
}
#ifdef FOMENT_WINDOWS
@@ -592,24 +603,31 @@ static void HandleSetPosition(FObject port, int64_t pos, FPositionFrom frm)
frm == FromBegin ? FILE_BEGIN : (frm == FromCurrent ? FILE_CURRENT : FILE_END));
}
+static FFileHandle HandleGetFileHandle(FObject port)
+{
+ FAssert(BinaryPortP(port) || TextualPortP(port));
+
+ return(AsGenericPort(port)->Context);
+}
+
static FObject MakeHandleInputPort(FObject nam, HANDLE h)
{
if (GetFileType(h) == FILE_TYPE_PIPE)
return(MakeBinaryPort(nam, NoValueObject, h, HandleCloseInput, 0, 0, HandleReadBytes,
- PipeByteReadyP, 0, 0, 0));
+ PipeByteReadyP, 0, 0, 0, HandleGetFileHandle, 0));
return(MakeBinaryPort(nam, NoValueObject, h, HandleCloseInput, 0, 0, HandleReadBytes,
- FileByteReadyP, 0, HandleGetPosition, HandleSetPosition));
+ FileByteReadyP, 0, HandleGetPosition, HandleSetPosition, HandleGetFileHandle, 0));
}
static FObject MakeHandleOutputPort(FObject nam, HANDLE h)
{
if (GetFileType(h) == FILE_TYPE_PIPE)
return(MakeBinaryPort(nam, NoValueObject, h, 0, HandleCloseOutput, HandleFlushOutput, 0, 0,
- HandleWriteBytes, 0, 0));
+ HandleWriteBytes, 0, 0, HandleGetFileHandle, 0));
return(MakeBinaryPort(nam, NoValueObject, h, 0, HandleCloseOutput, HandleFlushOutput, 0, 0,
- HandleWriteBytes, HandleGetPosition, HandleSetPosition));
+ HandleWriteBytes, HandleGetPosition, HandleSetPosition, HandleGetFileHandle, 0));
}
#endif // FOMENT_WINDOWS
@@ -680,26 +698,34 @@ static int64_t FileDescGetPosition(FObject port)
return(lseek((long_t) AsGenericPort(port)->Context, 0, SEEK_CUR));
}
+static FFileHandle FileDescGetFileHandle(FObject port)
+{
+ FAssert(BinaryPortP(port) || TextualPortP(port));
+
+ return((FFileHandle) (AsGenericPort(port)->Context));
+}
+
static void FileDescSetPosition(FObject port, int64_t pos, FPositionFrom frm)
{
FAssert(BinaryPortP(port) && PortOpenP(port) && PositioningPortP(port));
FAssert(frm == FromBegin || frm == FromCurrent || frm == FromEnd);
- lseek((long_t) AsGenericPort(port)->Context, pos,
+ lseek((long_t) AsGenericPort(port)->Context, pos,
frm == FromBegin ? SEEK_SET : (frm == FromCurrent ? SEEK_CUR : SEEK_END));
}
static FObject MakeFileDescInputPort(FObject nam, long_t fd)
{
return(MakeBinaryPort(nam, NoValueObject, (void *) fd, FileDescCloseInput, 0, 0,
- FileDescReadBytes, FileDescByteReadyP, 0, FileDescGetPosition, FileDescSetPosition));
+ FileDescReadBytes, FileDescByteReadyP, 0, FileDescGetPosition, FileDescSetPosition,
+ FileDescGetFileHandle, 0));
}
static FObject MakeFileDescOutputPort(FObject nam, long_t fd)
{
return(MakeBinaryPort(nam, NoValueObject, (void *) fd, 0, FileDescCloseOutput,
FileDescFlushOutput, 0, 0, FileDescWriteBytes, FileDescGetPosition,
- FileDescSetPosition));
+ FileDescSetPosition, FileDescGetFileHandle, 0));
}
#endif // FOMENT_UNIX
@@ -744,7 +770,7 @@ static FObject MakeBytevectorInputPort(FObject bv)
FAssert(BytevectorP(bv));
return(MakeBinaryPort(NoValueObject, bv, 0, BvinCloseInput, 0, 0, BvinReadBytes,
- BvinByteReadyP, 0, 0, 0));
+ BvinByteReadyP, 0, 0, 0, 0, 0));
}
static void BvoutCloseOutput(FObject port)
@@ -799,17 +825,16 @@ static FObject GetOutputBytevector(FObject port)
static FObject MakeBytevectorOutputPort()
{
- FObject port = MakeBinaryPort(NoValueObject, EmptyListObject, 0, 0, BvoutCloseOutput,
- BvoutFlushOutput, 0, 0, BvoutWriteBytes, 0, 0);
- AsGenericPort(port)->Flags |= PORT_FLAG_BYTEVECTOR_OUTPUT;
- return(port);
+ return(MakeBinaryPort(NoValueObject, EmptyListObject, 0, 0, BvoutCloseOutput,
+ BvoutFlushOutput, 0, 0, BvoutWriteBytes, 0, 0, 0, PORT_FLAG_BYTEVECTOR_OUTPUT));
}
// ---- Textual Ports ----
FObject MakeTextualPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn,
FCloseOutputFn cofn, FFlushOutputFn fofn, FReadChFn rcfn, FCharReadyPFn crpfn,
- FWriteStringFn wsfn, FGetPositionFn gpfn, FSetPositionFn spfn)
+ FWriteStringFn wsfn, FGetPositionFn gpfn, FSetPositionFn spfn, FGetFileHandleFn gfhfn,
+ ulong_t flgs)
{
FAssert((cifn == 0 && rcfn == 0 && crpfn == 0) || (cifn != 0 && rcfn != 0 && crpfn != 0));
FAssert((cofn == 0 && wsfn == 0 && fofn == 0) || (cofn != 0 && wsfn != 0 && fofn != 0));
@@ -820,7 +845,8 @@ FObject MakeTextualPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn
"%make-textual-port");
port->Generic.Flags = (cifn != 0 ? (PORT_FLAG_INPUT | PORT_FLAG_INPUT_OPEN) : 0)
| (cofn != 0 ? (PORT_FLAG_OUTPUT | PORT_FLAG_OUTPUT_OPEN) : 0)
- | (gpfn != 0 ? PORT_FLAG_POSITIONING : 0);
+ | (gpfn != 0 ? PORT_FLAG_POSITIONING : 0)
+ | flgs;
port->Generic.Name = nam;
port->Generic.Object = obj;
port->Generic.Context = ctx;
@@ -829,6 +855,7 @@ FObject MakeTextualPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn
port->Generic.FlushOutputFn = fofn;
port->Generic.GetPositionFn = gpfn;
port->Generic.SetPositionFn = spfn;
+ port->Generic.GetFileHandleFn = gfhfn == 0 ? InvalidFileHandle : gfhfn;
port->ReadChFn = rcfn;
port->CharReadyPFn = crpfn;
port->WriteStringFn = wsfn;
@@ -1099,6 +1126,13 @@ void SetPosition(FObject port, int64_t pos, FPositionFrom frm)
AsGenericPort(port)->SetPositionFn(port, pos, frm);
}
+FFileHandle GetFileHandle(FObject port)
+{
+ FAssert(BinaryPortP(port) || TextualPortP(port));
+
+ return(AsGenericPort(port)->GetFileHandleFn(port));
+}
+
static void TranslatorCloseInput(FObject port)
{
FAssert(BinaryPortP(AsGenericPort(port)->Object));
@@ -1132,7 +1166,7 @@ static FObject MakeTranslatorPort(FObject port, FReadChFn rcfn, FCharReadyPFn cr
OutputPortP(port) ? TranslatorFlushOutput : 0,
InputPortP(port) ? rcfn : 0,
InputPortP(port) ? crpfn : 0,
- OutputPortP(port) ? wsfn : 0, 0, 0));
+ OutputPortP(port) ? wsfn : 0, 0, 0, GetObjectFileHandle, 0));
}
static ulong_t AsciiReadCh(FObject port, FCh * ch)
@@ -1497,6 +1531,32 @@ FObject OpenOutputFile(FObject fn)
return(port);
}
+FObject OpenInputPipe(FFileHandle fh)
+{
+#ifdef FOMENT_WINDOWS
+ return(MakeLatin1Port(MakeBufferedPort(
+ MakeHandleInputPort(MakeStringC("input-pipe"), fh))));
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+ return(MakeUtf8Port(MakeBufferedPort(
+ MakeFileDescInputPort(MakeStringC("input-pipe"), fh))));
+#endif // FOMENT_UNIX
+}
+
+FObject OpenOutputPipe(FFileHandle fh)
+{
+#ifdef FOMENT_WINDOWS
+ return(MakeLatin1Port(MakeBufferedPort(
+ MakeHandleOutputPort(MakeStringC("output-pipe"), fh))));
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+ return(MakeUtf8Port(MakeBufferedPort(
+ MakeFileDescOutputPort(MakeStringC("output-pipe"), fh))));
+#endif // FOMENT_UNIX
+}
+
static void SinCloseInput(FObject port)
{
FAssert(TextualPortP(port));
@@ -1533,7 +1593,7 @@ FObject MakeStringInputPort(FObject s)
FAssert(StringP(s));
return(MakeTextualPort(NoValueObject, s, 0, SinCloseInput, 0, 0, SinReadCh, SinCharReadyP,
- 0, 0, 0));
+ 0, 0, 0, 0, 0));
}
static void SoutCloseOutput(FObject port)
@@ -1588,10 +1648,8 @@ FObject GetOutputString(FObject port)
FObject MakeStringOutputPort()
{
- FObject port = MakeTextualPort(NoValueObject, EmptyListObject, 0, 0, SoutCloseOutput,
- SoutFlushOutput, 0, 0, SoutWriteString, 0, 0);
- AsGenericPort(port)->Flags |= PORT_FLAG_STRING_OUTPUT;
- return(port);
+ return(MakeTextualPort(NoValueObject, EmptyListObject, 0, 0, SoutCloseOutput,
+ SoutFlushOutput, 0, 0, SoutWriteString, 0, 0, 0, PORT_FLAG_STRING_OUTPUT));
}
static void CinCloseInput(FObject port)
@@ -1622,7 +1680,7 @@ static long_t CinCharReadyP(FObject port)
FObject MakeStringCInputPort(const char * s)
{
return(MakeTextualPort(NoValueObject, NoValueObject, (void *) s, CinCloseInput, 0, 0,
- CinReadCh, CinCharReadyP, 0, 0, 0));
+ CinReadCh, CinCharReadyP, 0, 0, 0, 0, 0));
}
// ---- Console Input and Output ----
@@ -2761,6 +2819,13 @@ static long_t ConCharReadyP(FObject port)
}
#ifdef FOMENT_WINDOWS
+static FFileHandle ConGetFileHandle(FObject port)
+{
+ FAssert(InputPortP(port) && ConsolePortP(port));
+
+ return(AsConsoleInput(port)->InputHandle);
+}
+
static FObject MakeConsoleInputPort(FObject nam, HANDLE hin, HANDLE hout)
{
FConsoleInput * ci = MakeConsoleInput();
@@ -2771,8 +2836,7 @@ static FObject MakeConsoleInputPort(FObject nam, HANDLE hin, HANDLE hout)
ci->OutputHandle = hout;
FObject port = MakeTextualPort(nam, NoValueObject, ci, ConCloseInput, 0, 0, ConReadCh,
- ConCharReadyP, 0, 0, 0);
- AsGenericPort(port)->Flags |= PORT_FLAG_CONSOLE | PORT_FLAG_INTERACTIVE;
+ ConCharReadyP, 0, 0, 0, ConGetFileHandle, PORT_FLAG_CONSOLE | PORT_FLAG_INTERACTIVE);
AsConsoleInput(port)->Mode = CONSOLE_INPUT_ECHO;
return(port);
@@ -2806,15 +2870,19 @@ static void ConWriteString(FObject port, FCh * s, ulong_t sl)
static FObject MakeConsoleOutputPort(FObject nam, HANDLE h)
{
- FObject port = MakeTextualPort(nam, NoValueObject, h, 0, ConCloseOutput, ConFlushOutput, 0,
- 0, ConWriteString, 0, 0);
- AsGenericPort(port)->Flags |= PORT_FLAG_CONSOLE;
-
- return(port);
+ return(MakeTextualPort(nam, NoValueObject, h, 0, ConCloseOutput, ConFlushOutput, 0,
+ 0, ConWriteString, 0, 0, HandleGetFileHandle, PORT_FLAG_CONSOLE));
}
#endif // FOMENT_WINDOWS
#ifdef FOMENT_UNIX
+static FFileHandle ConGetFileHandle(FObject port)
+{
+ FAssert(InputPortP(port) && ConsolePortP(port));
+
+ return(AsConsoleInput(port)->InputFd);
+}
+
static FObject MakeConsoleInputPort(FObject nam, long_t ifd, long_t ofd, int fi)
{
FConsoleInput * ci = MakeConsoleInput();
@@ -2825,8 +2893,7 @@ static FObject MakeConsoleInputPort(FObject nam, long_t ifd, long_t ofd, int fi)
ci->OutputFd = ofd;
FObject port = MakeTextualPort(nam, NoValueObject, ci, ConCloseInput, 0, 0, ConReadCh,
- ConCharReadyP, 0, 0, 0);
- AsGenericPort(port)->Flags |= PORT_FLAG_CONSOLE;
+ ConCharReadyP, 0, 0, 0, ConGetFileHandle, PORT_FLAG_CONSOLE);
if (fi)
{
AsGenericPort(port)->Flags |= PORT_FLAG_INTERACTIVE;
@@ -2862,11 +2929,8 @@ static void ConWriteString(FObject port, FCh * s, ulong_t sl)
static FObject MakeConsoleOutputPort(FObject nam, long_t ofd)
{
- FObject port = MakeTextualPort(nam, NoValueObject, (void *) ofd, 0, ConCloseOutput,
- ConFlushOutput, 0, 0, ConWriteString, 0, 0);
- AsGenericPort(port)->Flags |= PORT_FLAG_CONSOLE;
-
- return(port);
+ return(MakeTextualPort(nam, NoValueObject, (void *) ofd, 0, ConCloseOutput,
+ ConFlushOutput, 0, 0, ConWriteString, 0, 0, FileDescGetFileHandle, PORT_FLAG_CONSOLE));
}
#endif // FOMENT_UNIX
@@ -3189,6 +3253,44 @@ Define("set-port-position!", SetPortPositionPrimitive)(long_t argc, FObject argv
return(NoValueObject);
}
+Define("%copy-port", CopyPortPrimitive)(long_t argc, FObject argv[])
+{
+ TwoArgsCheck("%copy-port", argc);
+ InputPortArgCheck("%copy-port", argv[0]);
+ if (TextualPortP(argv[0]))
+ {
+ TextualInputPortArgCheck("%copy-port", argv[0]);
+ TextualOutputPortArgCheck("%copy-port", argv[1]);
+
+ for (;;)
+ {
+ FCh ch;
+
+ if (ReadCh(argv[0], &ch) == 0)
+ break;
+ WriteCh(argv[1], ch);
+ }
+ }
+ else
+ {
+ FAssert(BinaryPortP(argv[0]));
+
+ BinaryInputPortArgCheck("%copy-port", argv[0]);
+ BinaryOutputPortArgCheck("%copy-port", argv[1]);
+
+ for (;;)
+ {
+ FByte buf[1024];
+ ulong_t bl = ReadBytes(argv[0], buf, sizeof(buf));
+ if (bl == 0)
+ break;
+ WriteBytes(argv[1], buf, bl);
+ }
+ }
+
+ return(NoValueObject);
+}
+
Define("socket-merge-flags", SocketMergeFlagsPrimitive)(long_t argc, FObject argv[])
{
long_t ret = 0;
@@ -3583,6 +3685,7 @@ static FObject Primitives[] =
PositioningPortPPrimitive,
PortPositionPrimitive,
SetPortPositionPrimitive,
+ CopyPortPrimitive,
SocketMergeFlagsPrimitive,
SocketPurgeFlagsPrimitive,
SocketPPrimitive,
@@ -3599,25 +3702,43 @@ static FObject Primitives[] =
#ifdef FOMENT_UNIX
static struct termios OriginalTios;
+static int InteractiveConsole = 0;
-static void FomentAtExit(void)
-{
- tcsetattr(0, TCSANOW, &OriginalTios);
-}
-
-static long_t SetupConsole()
+static void SetupTios(void)
{
struct termios tios;
- tcgetattr(0, &OriginalTios);
- atexit(FomentAtExit);
-
tcgetattr(0, &tios);
tios.c_iflag = BRKINT;
tios.c_lflag = ISIG;
tios.c_cc[VMIN] = 1;
tios.c_cc[VTIME] = 1;
tcsetattr(0, TCSANOW, &tios);
+}
+
+static void RestoreTios(void)
+{
+ tcsetattr(0, TCSANOW, &OriginalTios);
+}
+
+void SetupConsoleAgain()
+{
+ if (InteractiveConsole)
+ SetupTios();
+}
+
+void RestoreConsole()
+{
+ if (InteractiveConsole)
+ RestoreTios();
+}
+
+static long_t SetupConsole()
+{
+ tcgetattr(0, &OriginalTios);
+ atexit(RestoreTios);
+
+ SetupTios();
long_t x;
long_t y;
@@ -3634,7 +3755,7 @@ static void DefineConstant(FObject env, FObject lib, const char * nam, FObject v
LibraryExport(lib, EnvironmentSetC(env, nam, val));
}
-void ExitFoment()
+void FlushStandardPorts()
{
if (TextualPortP(StandardOutput) || BinaryPortP(StandardOutput))
FlushOutput(StandardOutput);
@@ -3693,8 +3814,9 @@ void SetupIO()
if (isatty(0) && isatty(1))
{
char * term = getenv("TERM");
- int fi = (term != 0 && strcasecmp(term, "dumb") != 0 && SetupConsole());
- StandardInput = MakeConsoleInputPort(MakeStringC("console-input"), 0, 1, fi);
+ InteractiveConsole = (term != 0 && strcasecmp(term, "dumb") != 0 && SetupConsole());
+ StandardInput = MakeConsoleInputPort(MakeStringC("console-input"), 0, 1,
+ InteractiveConsole);
StandardOutput = MakeConsoleOutputPort(MakeStringC("console-output"), 1);
}
else
diff --git a/src/io.hpp b/src/io.hpp
index bb5b8a9..7ca08ba 100644
--- a/src/io.hpp
+++ b/src/io.hpp
@@ -27,7 +27,8 @@ typedef struct
FObject MakeBinaryPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn,
FCloseOutputFn cofn, FFlushOutputFn fofn, FReadBytesFn rbfn, FByteReadyPFn brpfn,
- FWriteBytesFn wbfn, FGetPositionFn gpfn, FSetPositionFn spfn);
+ FWriteBytesFn wbfn, FGetPositionFn gpfn, FSetPositionFn spfn,
+ FGetFileHandleFn gfhfn, ulong_t flgs);
// ---- Textual Ports ----
@@ -50,7 +51,8 @@ typedef struct
FObject MakeTextualPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn,
FCloseOutputFn cofn, FFlushOutputFn fofn, FReadChFn rcfn, FCharReadyPFn crpfn,
- FWriteStringFn wsfn, FGetPositionFn gpfn, FSetPositionFn spfn);
+ FWriteStringFn wsfn, FGetPositionFn gpfn, FSetPositionFn spfn, FGetFileHandleFn gfhfn,
+ ulong_t flgs);
inline FObject CurrentInputPort()
{
@@ -74,10 +76,18 @@ inline FObject CurrentOutputPort()
return(port);
}
+FObject OpenInputPipe(FFileHandle fh);
+FObject OpenOutputPipe(FFileHandle fh);
+
// ----------------
void SetupWrite();
void SetupRead();
long_t IdentifierSubsequentP(FCh ch);
+#ifdef FOMENT_UNIX
+void SetupConsoleAgain();
+void RestoreConsole();
+#endif // FOMENT_UNIX
+
#endif // __IO_HPP__
diff --git a/src/main.cpp b/src/main.cpp
index 5a63693..c1a6e24 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -155,7 +155,7 @@ static int RunProgram(FChS * arg)
ReadLine(port);
ExecuteProc(CompileProgram(nam, port));
- ExitFoment();
+ FlushStandardPorts();
return(0);
}
@@ -769,7 +769,7 @@ int main(int argc, FChS * argv[])
if ((BinaryPortP(StandardOutput) || TextualPortP(StandardOutput))
&& OutputPortOpenP(StandardOutput))
WriteSimple(StandardOutput, obj, 0);
- ExitFoment();
+ FlushStandardPorts();
return(1);
}
@@ -779,7 +779,7 @@ int main(int argc, FChS * argv[])
{
if (ProcessOptions(LateConfig, argc, argv, &pdx) == 0)
{
- ExitFoment();
+ FlushStandardPorts();
return(1);
}
@@ -811,12 +811,12 @@ int main(int argc, FChS * argv[])
return(1);
}
ExecuteProc(CompileProgram(AsGenericPort(StandardInput)->Name, StandardInput));
- ExitFoment();
+ FlushStandardPorts();
break;
case InteractiveMode:
ExecuteProc(InteractiveThunk);
- ExitFoment();
+ FlushStandardPorts();
break;
default:
@@ -831,7 +831,7 @@ int main(int argc, FChS * argv[])
WriteStringC(StandardOutput, "exception: ");
WriteSimple(StandardOutput, obj, 0);
WriteCh(StandardOutput, '\n');
- ExitFoment();
+ FlushStandardPorts();
return(1);
}
}
diff --git a/src/process.cpp b/src/process.cpp
new file mode 100644
index 0000000..f67ff57
--- /dev/null
+++ b/src/process.cpp
@@ -0,0 +1,558 @@
+/*
+
+Foment
+
+- %subprocess: textual vs binary port when a pipe is created
+*/
+
+#ifdef FOMENT_WINDOWS
+#include <windows.h>
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif // FOMENT_UNIX
+
+#include <stdio.h>
+#include "foment.hpp"
+#include "syncthrd.hpp"
+#include "io.hpp"
+#include "unicode.hpp"
+
+#if defined(FOMENT_BSD) || defined(FOMENT_OSX)
+extern char ** environ;
+#else
+#include <malloc.h>
+#endif
+
+// ----------------
+
+EternalSymbol(StdoutSymbol, "stdout");
+
+// ---- Subprocesses ----
+
+typedef struct
+{
+#ifdef FOMENT_WINDOWS
+ DWORD ProcessID;
+ HANDLE ProcessHandle;
+ DWORD Status;
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+ pid_t ProcessID;
+ int Status;
+ int Done;
+#endif // FOMENT_UNIX
+} FSubprocess;
+
+#define AsSubprocess(obj) ((FSubprocess *) (obj))
+#define SubprocessP(obj) (IndirectTag(obj) == SubprocessTag)
+
+#ifdef FOMENT_WINDOWS
+static FObject MakeSubprocess(DWORD pid, HANDLE ph, const char * who)
+{
+ FSubprocess * sub = (FSubprocess *) MakeObject(SubprocessTag, sizeof(FSubprocess), 0, who);
+ sub->ProcessID = pid;
+ sub->ProcessHandle = ph;
+ return(sub);
+}
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+static FObject MakeSubprocess(pid_t pid, const char * who)
+{
+ FSubprocess * sub = (FSubprocess *) MakeObject(SubprocessTag, sizeof(FSubprocess), 0, who);
+ sub->ProcessID = pid;
+ sub->Status = 0;
+ sub->Done = 0;
+ return(sub);
+}
+#endif // FOMENT_UNIX
+
+void WriteSubprocess(FWriteContext * wctx, FObject obj)
+{
+ FCh s[16];
+ long_t sl = FixnumAsString((long_t) obj, s, 16);
+
+ wctx->WriteStringC("#<subprocess: ");
+ wctx->WriteString(s, sl);
+ wctx->WriteStringC(" pid: ");
+
+ FAssert(SubprocessP(obj));
+
+ sl = FixnumAsString((long_t) AsSubprocess(obj)->ProcessID, s, 10);
+ wctx->WriteString(s, sl);
+ wctx->WriteCh('>');
+}
+
+inline void SubprocessArgCheck(const char * who, FObject obj)
+{
+ if (SubprocessP(obj) == 0)
+ RaiseExceptionC(Assertion, who, "expected a subprocess", List(obj));
+}
+
+static void MakePipe(FFileHandle * ifh, FFileHandle * ofh, const char * who)
+{
+#ifdef FOMENT_WINDOWS
+ if (CreatePipe(ifh, ofh, 0, 0) == 0)
+ RaiseExceptionC(Assertion, who, "create pipe failed", List(MakeFixnum(GetLastError())));
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+ int pipefd[2];
+
+ if (pipe(pipefd) != 0)
+ RaiseExceptionC(Assertion, who, "pipe system call failed", List(MakeFixnum(errno)));
+ *ifh = pipefd[0];
+ *ofh = pipefd[1];
+#endif // FOMENT_UNIX
+}
+
+static FChS * ConvertStringToSystem(FObject s)
+{
+ FAssert(StringP(s));
+
+#ifdef FOMENT_WINDOWS
+ FObject bv = ConvertStringToUtf16(s);
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+ FObject bv = ConvertStringToUtf8(s);
+#endif // FOMENT_UNIX
+
+ FAssert(BytevectorP(bv));
+
+ return((FChS *) AsBytevector(bv)->Vector);
+}
+
+typedef struct
+{
+ // Optional file handles to use for stdout, stdin, and stderr in the subprocess.
+ FFileHandle ChildStdout; // Output file handle.
+ FFileHandle ChildStdin; // Input file handle.
+ FFileHandle ChildStderr; // Output file handle.
+ int UseStdout; // Use stdout for stderr.
+
+ // These are only used if the corresponding Child* above is not specified.
+ FFileHandle PipeStdout; // Input file handle of a pipe connected to stdout in the subprocess.
+ FFileHandle PipeStdin; // Output file handle of a pipe connected to stdin in the subprocess.
+ FFileHandle PipeStderr; // Input file handle of a pipe connected to stderr in the subprocess.
+} FSubprocessFileHandles;
+
+#ifdef FOMENT_WINDOWS
+static FChS * CopyArg(FChS * cmd, FChS * arg)
+{
+ *cmd = '"';
+ cmd += 1;
+
+ while (*arg != 0)
+ {
+ if (*arg == '"') // || *arg == '\\')
+ {
+ *cmd = '\\';
+ cmd += 1;
+ }
+ *cmd = *arg;
+ cmd += 1;
+ arg += 1;
+ }
+
+ *cmd = '"';
+ cmd += 1;
+ return(cmd);
+}
+
+static void ChildArgs(long_t argc, FObject argv[], FChS ** app, FChS ** cmdline)
+{
+ long_t cmdlen = 1;
+ long_t adx;
+
+ for (adx = 0; adx < argc; adx++)
+ cmdlen += StringLength(argv[adx]) * 2 + 3;
+
+ FObject bv = MakeBytevector(cmdlen * sizeof(FChS));
+ FAssert(BytevectorP(bv));
+ FChS * cmd = (FChS *) AsBytevector(bv)->Vector;
+ *cmdline = cmd;
+
+ for (adx = 0; adx < argc; adx++)
+ {
+ FChS * arg = ConvertStringToSystem(argv[adx]);
+ if (adx == 0)
+ {
+ *app = arg;
+ FChS * s = wcsrchr(arg, '\\');
+ if (s != 0)
+ arg = s + 1;
+ cmd = CopyArg(cmd, arg);
+ }
+ else
+ {
+ *cmd = ' ';
+ cmd += 1;
+ cmd = CopyArg(cmd, arg);
+ }
+ }
+ *cmd = 0;
+}
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+static void ChildFailed(char * cmd, const char * call)
+{
+ fprintf(stderr, "error: %s: %s: %d\n", cmd, call, errno);
+ fflush(stderr);
+ exit(1);
+}
+
+static void ChildProcess(FSubprocessFileHandles * sfh, FFileHandle chldin, FFileHandle chldout,
+ FFileHandle chlderr, long_t argc, FObject argv[])
+{
+ FAssert(argc > 0);
+
+ char ** args = (char **) malloc(sizeof(char *) * (argc + 1));
+ char * cmd = ConvertStringToSystem(argv[0]);
+ char * s = strrchr(cmd, '/');
+ if (s == 0)
+ args[0] = cmd;
+ else
+ args[0] = strdup(s + 1);
+
+ for (long_t adx = 1; adx < argc; adx += 1)
+ args[adx] = ConvertStringToSystem(argv[adx]);
+ args[argc] = 0;
+
+ if (sfh->PipeStdout != INVALID_FILE_HANDLE)
+ close(sfh->PipeStdout);
+ if (sfh->PipeStdin != INVALID_FILE_HANDLE)
+ close(sfh->PipeStdin);
+ if (sfh->UseStdout == 0 && sfh->PipeStderr != INVALID_FILE_HANDLE)
+ close(sfh->PipeStderr);
+
+ while (chldin < 3)
+ chldin = dup(chldin);
+ while (chldout < 3)
+ chldout = dup(chldout);
+ while (chlderr < 3)
+ chlderr = dup(chlderr);
+
+ close(0);
+ if (dup2(chldin, 0) != 0)
+ ChildFailed(cmd, "dup2");
+ close(1);
+ if (dup2(chldout, 1) != 1)
+ ChildFailed(cmd, "dup2");
+ close(2);
+ if (dup2(chlderr, 2) != 2)
+ ChildFailed(cmd, "dup2");
+ close(chldin);
+ close(chldout);
+ close(chlderr);
+
+ execve(cmd, args, environ);
+ ChildFailed(cmd, "execve");
+}
+#endif // FOMENT_UNIX
+
+static FObject Subprocess(FSubprocessFileHandles * sfh, long_t argc, FObject argv[],
+ const char * who)
+{
+ FFileHandle chldout, chldin, chlderr;
+
+ if (sfh->ChildStdout == INVALID_FILE_HANDLE)
+ {
+ MakePipe(&(sfh->PipeStdout), &chldout, who);
+#ifdef FOMENT_WINDOWS
+ SetHandleInformation(chldout, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+#endif // FOMENT_WINDOWS
+ }
+ else
+ chldout = sfh->ChildStdout;
+
+ if (sfh->ChildStdin == INVALID_FILE_HANDLE)
+ {
+ MakePipe(&chldin, &(sfh->PipeStdin), who);
+#ifdef FOMENT_WINDOWS
+ SetHandleInformation(chldin, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+#endif // FOMENT_WINDOWS
+ }
+ else
+ chldin = sfh->ChildStdin;
+
+ if (sfh->UseStdout)
+ chlderr = chldout;
+ else if (sfh->ChildStderr == INVALID_FILE_HANDLE)
+ {
+ MakePipe(&(sfh->PipeStderr), &chlderr, who);
+#ifdef FOMENT_WINDOWS
+ SetHandleInformation(chlderr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+#endif // FOMENT_WINDOWS
+ }
+ else
+ chlderr = sfh->ChildStderr;
+
+ FlushStandardPorts();
+
+#ifdef FOMENT_WINDOWS
+ FChS *app;
+ FChS *cmdline;
+ ChildArgs(argc, argv, &app, &cmdline);
+
+ STARTUPINFOW si;
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = chldin;
+ si.hStdOutput = chldout;
+ si.hStdError = chlderr;
+
+ PROCESS_INFORMATION pi;
+ BOOL ret = CreateProcessW(app, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
+ if (ret == 0)
+ RaiseExceptionC(Assertion, who, "create process system call failed",
+ List(MakeIntegerFromUInt64(GetLastError())));
+
+ CloseHandle(pi.hThread);
+
+ if (sfh->PipeStdout != INVALID_FILE_HANDLE)
+ CloseHandle(chldout);
+
+ if (sfh->PipeStdin != INVALID_FILE_HANDLE)
+ CloseHandle(chldin);
+
+ if (sfh->UseStdout == 0 && sfh->PipeStderr != INVALID_FILE_HANDLE)
+ CloseHandle(chlderr);
+
+ return(MakeSubprocess(pi.dwProcessId, pi.hProcess, who));
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+ RestoreConsole();
+
+ pid_t pid = fork();
+ if (pid < 0)
+ RaiseExceptionC(Assertion, who, "fork system call failed", List(MakeFixnum(errno)));
+
+ if (pid == 0)
+ {
+ ChildProcess(sfh, chldin, chldout, chlderr, argc, argv);
+
+ // Never returns.
+ FAssert(0);
+ }
+
+ // Parent.
+
+ SetupConsoleAgain();
+
+ if (sfh->PipeStdout != INVALID_FILE_HANDLE)
+ close(chldout);
+
+ if (sfh->PipeStdin != INVALID_FILE_HANDLE)
+ close(chldin);
+
+ if (sfh->UseStdout == 0 && sfh->PipeStderr != INVALID_FILE_HANDLE)
+ close(chlderr);
+
+ return(MakeSubprocess(pid, who));
+#endif // FOMENT_UNIX
+}
+
+Define("%subprocess", SubprocessPrimitive)(long_t argc, FObject argv[])
+{
+ AtLeastFiveArgsCheck("%subprocess", argc);
+ BooleanArgCheck("%subprocess", argv[0]);
+
+ for (long_t adx = 4; adx < argc; adx += 1)
+ StringArgCheck("%subprocess", argv[adx]);
+
+ FSubprocessFileHandles sfh;
+
+ if (argv[1] == FalseObject)
+ sfh.ChildStdout = INVALID_FILE_HANDLE;
+ else
+ {
+ OutputPortArgCheck("%subprocess", argv[1]);
+
+ sfh.ChildStdout = GetFileHandle(argv[1]);
+ if (argv[0] == FalseObject && sfh.ChildStdout == INVALID_FILE_HANDLE)
+ RaiseExceptionC(Assertion, "%subprocess", "expected an output file handle port",
+ List(argv[1]));
+ }
+
+ if (argv[2] == FalseObject)
+ sfh.ChildStdin = INVALID_FILE_HANDLE;
+ else
+ {
+ InputPortArgCheck("%subprocess", argv[2]);
+
+ sfh.ChildStdin = GetFileHandle(argv[2]);
+ if (argv[0] == FalseObject && sfh.ChildStdin == INVALID_FILE_HANDLE)
+ RaiseExceptionC(Assertion, "%subprocess", "expected an input file handle port",
+ List(argv[2]));
+ }
+
+ sfh.ChildStderr = INVALID_FILE_HANDLE;
+ sfh.UseStdout = 0;
+
+ if (argv[3] == StdoutSymbol)
+ sfh.UseStdout = 1;
+ else if (argv[3] != FalseObject)
+ {
+ OutputPortArgCheck("%subprocess", argv[3]);
+
+ sfh.ChildStderr = GetFileHandle(argv[3]);
+ if (argv[0] == FalseObject && sfh.ChildStderr == INVALID_FILE_HANDLE)
+ RaiseExceptionC(Assertion, "%subprocess", "expected an output file handle port",
+ List(argv[3]));
+ }
+
+ sfh.PipeStdout = INVALID_FILE_HANDLE;
+ sfh.PipeStdin = INVALID_FILE_HANDLE;
+ sfh.PipeStderr = INVALID_FILE_HANDLE;
+
+ FObject sub = Subprocess(&sfh, argc - 4, argv + 4, "%subprocess");
+
+ FAssert(SubprocessP(sub));
+
+ FObject out = (sfh.ChildStdout == INVALID_FILE_HANDLE) ? OpenInputPipe(sfh.PipeStdout) :
+ FalseObject;
+
+ FObject in = (sfh.ChildStdin == INVALID_FILE_HANDLE) ? OpenOutputPipe(sfh.PipeStdin) :
+ FalseObject;
+
+ FObject err = (sfh.UseStdout == 0 && sfh.ChildStderr == INVALID_FILE_HANDLE) ?
+ OpenInputPipe(sfh.PipeStderr) : FalseObject;
+
+ return(List(sub, out, in, err));
+}
+
+Define("subprocess?", SubprocessPPrimitive)(long_t argc, FObject argv[])
+{
+ OneArgCheck("subprocess?", argc);
+
+ return(SubprocessP(argv[0]) ? TrueObject : FalseObject);
+}
+
+Define("subprocess-wait", SubprocessWaitPrimitive)(long_t argc, FObject argv[])
+{
+ OneArgCheck("subprocess-wait", argc);
+ SubprocessArgCheck("subprocess-wait", argv[0]);
+
+ FSubprocess * sub = AsSubprocess(argv[0]);
+
+#ifdef FOMENT_WINDOWS
+ if (sub->ProcessHandle != INVALID_HANDLE_VALUE)
+ {
+ if (WaitForSingleObject(sub->ProcessHandle, INFINITE) == WAIT_FAILED)
+ RaiseExceptionC(Assertion, "subprocess-wait",
+ "wait for single object system call failed",
+ List(MakeIntegerFromUInt64(GetLastError())));
+
+ DWORD status;
+ GetExitCodeProcess(sub->ProcessHandle, &status);
+ sub->Status = status;
+
+ CloseHandle(sub->ProcessHandle);
+ sub->ProcessHandle = INVALID_HANDLE_VALUE;
+ }
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+ if (sub->Done == 0)
+ {
+ int status;
+ if (waitpid(sub->ProcessID, &status, 0) < 0)
+ RaiseExceptionC(Assertion, "subprocess-wait", "waitpid failed",
+ List(MakeFixnum(errno)));
+
+ sub->Done = 1;
+ sub->Status = status;
+ }
+#endif // FOMENT_UNIX
+
+ return(NoValueObject);
+}
+
+Define("subprocess-status", SubprocessStatusPrimitive)(long_t argc, FObject argv[])
+{
+ OneArgCheck("subprocess-status", argc);
+ SubprocessArgCheck("subprocess-status", argv[0]);
+
+ FSubprocess * sub = AsSubprocess(argv[0]);
+
+#ifdef FOMENT_WINDOWS
+ if (sub->ProcessHandle != INVALID_HANDLE_VALUE)
+ return(StringCToSymbol("running"));
+
+ return(MakeIntegerFromUInt64(sub->Status));
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+ if (sub->Done == 0)
+ return(StringCToSymbol("running"));
+
+ if (WIFEXITED(sub->Status))
+ return(MakeFixnum(WEXITSTATUS(sub->Status)));
+
+ FAssert(WIFSIGNALED(sub->Status));
+
+ return(MakeFixnum(WTERMSIG(sub->Status)));
+#endif // FOMENT_UNIX
+}
+
+Define("subprocess-kill", SubprocessKillPrimitive)(long_t argc, FObject argv[])
+{
+ TwoArgsCheck("subprocess-kill", argc);
+ SubprocessArgCheck("subprocess-kill", argv[0]);
+
+ FSubprocess * sub = AsSubprocess(argv[0]);
+
+#ifdef FOMENT_WINDOWS
+ if (sub->ProcessHandle != INVALID_HANDLE_VALUE && argv[1] != FalseObject)
+ TerminateProcess(sub->ProcessHandle, 0);
+#endif // FOMENT_WINDOWS
+
+#ifdef FOMENT_UNIX
+ if (sub->Done == 0)
+ kill(sub->ProcessID, argv[1] == FalseObject ? SIGINT : SIGTERM);
+#endif // FOMENT_UNIX
+
+ return(NoValueObject);
+}
+
+Define("subprocess-pid", SubprocessPIDPrimitive)(long_t argc, FObject argv[])
+{
+ OneArgCheck("subprocess-pid", argc);
+ SubprocessArgCheck("subprocess-pid", argv[0]);
+
+ return(MakeFixnum(AsSubprocess(argv[0])->ProcessID));
+}
+
+static FObject Primitives[] =
+{
+ SubprocessPrimitive,
+ SubprocessPPrimitive,
+ SubprocessWaitPrimitive,
+ SubprocessStatusPrimitive,
+ SubprocessKillPrimitive,
+ SubprocessPIDPrimitive
+};
+
+void SetupProcess()
+{
+ StdoutSymbol = InternSymbol(StdoutSymbol);
+
+ FAssert(StdoutSymbol == StringCToSymbol("stdout"));
+
+ for (ulong_t idx = 0; idx < sizeof(Primitives) / sizeof(FPrimitive *); idx++)
+ DefinePrimitive(Bedrock, BedrockLibrary, Primitives[idx]);
+}
diff --git a/src/syncthrd.cpp b/src/syncthrd.cpp
index 384e021..108e68e 100644
--- a/src/syncthrd.cpp
+++ b/src/syncthrd.cpp
@@ -253,7 +253,7 @@ void ThreadExit(FObject obj)
if (LeaveThread(ts) == 0)
{
- ExitFoment();
+ FlushStandardPorts();
exit(0);
}
@@ -281,7 +281,7 @@ Define("%exit", ExitPrimitive)(long_t argc, FObject argv[])
{
ZeroOrOneArgsCheck("exit", argc);
- ExitFoment();
+ FlushStandardPorts();
if (argc == 0 || argv[0] == TrueObject)
exit(0);
if (FixnumP(argv[0]))
diff --git a/test/exitcode.cpp b/test/exitcode.cpp
new file mode 100644
index 0000000..a178ee4
--- /dev/null
+++ b/test/exitcode.cpp
@@ -0,0 +1,14 @@
+/*
+
+Exit with the code specified as an argument.
+
+*/
+
+#include <stdlib.h>
+
+int main(int argc, char * argv[])
+{
+ if (argc != 2)
+ return(1);
+ return(atoi(argv[1]));
+}
diff --git a/test/hang.cpp b/test/hang.cpp
new file mode 100644
index 0000000..a09ecde
--- /dev/null
+++ b/test/hang.cpp
@@ -0,0 +1,15 @@
+/*
+
+Hang.
+
+*/
+
+int main(int argc, char * argv[])
+{
+ int cnt = 0;
+
+ for (;;)
+ cnt += 1;
+
+ return(0);
+}
diff --git a/test/process.scm b/test/process.scm
new file mode 100644
index 0000000..62a9383
--- /dev/null
+++ b/test/process.scm
@@ -0,0 +1,311 @@
+;;;
+;;; Processes
+;;;
+
+(import (foment base))
+
+(check-equal #t #t)
+
+(define (read-lines port)
+ (let ((s (read-line port)))
+ (if (eof-object? s)
+ '()
+ (cons s (read-lines port)))))
+
+(define stdread
+ (cond-expand
+ (windows "..\\windows\\debug\\stdread.exe")
+ (else "../unix/debug/stdread")))
+
+(define stdwrite
+ (cond-expand
+ (windows "..\\windows\\debug\\stdwrite.exe")
+ (else "../unix/debug/stdwrite")))
+
+(define exitcode
+ (cond-expand
+ (windows "..\\windows\\debug\\exitcode.exe")
+ (else "../unix/debug/exitcode")))
+
+(define hang
+ (cond-expand
+ (windows "..\\windows\\debug\\hang.exe")
+ (else "../unix/debug/hang")))
+
+;; subprocess
+
+(check-equal 0
+ (call-with-values
+ (lambda ()
+ (subprocess (current-output-port) #f (current-error-port)
+ stdread "ABC" "DEF GHI" "JKLMNOPQR"))
+ (lambda (sub in out err)
+ (for-each
+ (lambda (line)
+ (display line out)
+ (newline out))
+ '("ABC" "DEF GHI" "JKLMNOPQR"))
+ (close-port out)
+ (subprocess-wait sub)
+ (subprocess-status sub))))
+
+(check-equal ("abc" "def\\ghi" "jklmn\"opqr")
+ (call-with-values
+ (lambda ()
+ (subprocess #f (current-input-port) (current-error-port)
+ stdwrite "--stdout" "abc" "def\\ghi" "jklmn\"opqr"))
+ (lambda (sub in out err)
+ (let ((lines (read-lines in)))
+ (close-port in)
+ (subprocess-wait sub)
+ (if (zero? (subprocess-status sub))
+ lines
+ #f)))))
+
+(check-equal ("abc" "defghi" "jklmnopqr")
+ (call-with-values
+ (lambda ()
+ (subprocess #f (current-input-port) 'stdout
+ stdwrite "--stderr" "abc" "defghi" "jklmnopqr"))
+ (lambda (sub in out err)
+ (let ((lines (read-lines in)))
+ (close-port in)
+ (subprocess-wait sub)
+ (if (zero? (subprocess-status sub))
+ lines
+ #f)))))
+
+(check-equal ("abc" "defghi" "jklmnopqr")
+ (call-with-values
+ (lambda ()
+ (subprocess (current-output-port) (current-input-port) #f
+ stdwrite "--stderr" "abc" "defghi" "jklmnopqr"))
+ (lambda (sub in out err)
+ (let ((lines (read-lines err)))
+ (close-port err)
+ (subprocess-wait sub)
+ (if (zero? (subprocess-status sub))
+ lines
+ #f)))))
+
+(check-equal 234
+ (call-with-values
+ (lambda ()
+ (subprocess (current-output-port) (current-input-port) (current-error-port)
+ exitcode "234"))
+ (lambda (sub in out err)
+ (subprocess-wait sub)
+ (subprocess-status sub))))
+
+;; subprocess-wait
+;; subprocess-status
+;; subprocess-kill
+;; subprocess-pid
+;; subprocess?
+
+(define-values (sub1 in1 out1 err1) (subprocess #f #f 'stdout hang))
+(check-equal #t (subprocess? sub1))
+(check-equal #f (subprocess? in1))
+(check-equal #f err1)
+(check-equal #t (input-port-open? in1))
+(check-equal #t (output-port-open? out1))
+
+(check-equal running (subprocess-status sub1))
+(check-equal #t (and (integer? (subprocess-pid sub1)) (> (subprocess-pid sub1) 0)))
+
+(subprocess-kill sub1 #t)
+(subprocess-wait sub1)
+(check-equal #t (integer? (subprocess-status sub1)))
+
+(close-port in1)
+(close-port out1)
+
+;; process/ports
+;; process*/ports
+
+(check-equal 0
+ (let* ((lst (process*/ports (current-output-port) #f (current-error-port)
+ stdread "ABC" "DEFGHI" "JKLMNOPQR"))
+ (out (cadr lst))
+ (ctrl (cadddr (cdr lst))))
+ (for-each
+ (lambda (line)
+ (display line out)
+ (newline out))
+ '("ABC" "DEFGHI" "JKLMNOPQR"))
+ (close-port out)
+ (ctrl 'wait)
+ (ctrl 'exit-code)))
+
+(check-equal ("abc" "defghi" "jklmnopqr")
+ (let* ((lst (process*/ports #f (current-input-port) (current-error-port)
+ stdwrite "--stdout" "abc" "defghi" "jklmnopqr"))
+ (in (car lst))
+ (ctrl (cadddr (cdr lst))))
+ (let ((lines (read-lines in)))
+ (close-port in)
+ (ctrl 'wait)
+ (if (zero? (ctrl 'exit-code))
+ lines
+ #f))))
+
+(check-equal ("abc" "defghi" "jklmnopqr")
+ (let* ((lst (process/ports #f (current-input-port) 'stdout
+ "stdwrite --stderr abc defghi jklmnopqr"))
+ (in (car lst))
+ (ctrl (cadddr (cdr lst))))
+ (let ((lines (read-lines in)))
+ (close-port in)
+ (ctrl 'wait)
+ (if (zero? (ctrl 'exit-code))
+ lines
+ #f))))
+
+(check-equal ("abc" "defghi" "jklmnopqr")
+ (let* ((lst (process*/ports (current-output-port) (current-input-port) #f
+ stdwrite "--stderr" "abc" "defghi" "jklmnopqr"))
+ (err (cadddr lst))
+ (ctrl (cadddr (cdr lst))))
+ (let ((lines (read-lines err)))
+ (close-port err)
+ (ctrl 'wait)
+ (if (zero? (ctrl 'exit-code))
+ lines
+ #f))))
+
+(check-equal 123
+ (let* ((lst (process*/ports (current-output-port) (current-input-port) (current-error-port)
+ exitcode "123"))
+ (ctrl (cadddr (cdr lst))))
+ (ctrl 'wait)
+ (ctrl 'exit-code)))
+
+;; (<ctrl> 'status)
+;; (<ctrl> 'exit-code)
+;; (<ctrl> 'wait)
+;; (<ctrl> 'kill)
+
+(define p2 (process*/ports #f #f 'stdout hang))
+(define in2 (car p2))
+(define out2 (cadr p2))
+(check-equal #t
+ (let ((pid (caddr p2)))
+ (and (integer? pid) (> pid 0))))
+(define err2 (cadddr p2))
+(define ctrl2 (cadddr (cdr p2)))
+
+(check-equal #f err2)
+(check-equal #t (input-port-open? in2))
+(check-equal #t (output-port-open? out2))
+
+(check-equal running (ctrl2 'status))
+(check-equal #f (ctrl2 'exit-code))
+
+(ctrl2 'kill)
+(ctrl2 'wait)
+(check-equal #t
+ (eq? (cond-expand (windows 'done-ok) (else 'done-error))
+ (ctrl2 'status)))
+
+(check-equal #t (integer? (ctrl2 'exit-code)))
+
+;; system
+;; system*
+
+(define (with-output-to-string proc)
+ (let ((port (open-output-string)))
+ (parameterize ((current-output-port port)) (proc))
+ (get-output-string port)))
+
+(check-equal #t
+ (string=? (substring "abcdefghijklmnopqr" 0 18)
+ (substring
+ (with-output-to-string
+ (lambda ()
+ (system "stdwrite --stdout abcdefghijklmnopqr")))
+ 0 18)))
+
+(define (with-input-from-string s proc)
+ (let ((port (open-input-string s)))
+ (parameterize ((current-input-port port)) (proc))))
+
+(check-equal #t
+ (with-input-from-string "abcdef\n"
+ (lambda ()
+ (system* stdread "abcdef"))))
+
+(check-equal #f
+ (with-input-from-string "abcdef"
+ (lambda ()
+ (system* stdread "abc" "def"))))
+
+;; system/exit-code
+;; system*/exit-code
+
+(check-equal 88
+ (system/exit-code "exitcode 88"))
+
+(check-equal 88
+ (system*/exit-code exitcode "88"))
+
+;; chicken process module
+
+(define (call-with-input-pipe cmdline proc)
+ (let* ((lst (process/ports #f (current-input-port) (current-error-port) cmdline))
+ (in (car lst))
+ (ctrl (cadddr (cdr lst))))
+ (let-values ((results (proc in)))
+ (close-port in)
+ (ctrl 'wait)
+ (if (= (ctrl 'exit-code) 0)
+ (apply values results)
+ #f))))
+
+(check-equal ("aaa" "bbb" "ccc" "ddd")
+ (call-with-input-pipe "stdwrite --stdout aaa bbb ccc ddd"
+ (lambda (in) (read-lines in))))
+
+(define (call-with-output-pipe cmdline proc)
+ (let* ((lst (process/ports (current-output-port) #f (current-error-port) cmdline))
+ (out (cadr lst))
+ (ctrl (cadddr (cdr lst))))
+ (let-values ((results (proc out)))
+ (close-port out)
+ (ctrl 'wait)
+ (if (= (ctrl 'exit-code) 0)
+ (apply values results)
+ #f))))
+
+(check-equal #t
+ (call-with-output-pipe "stdread 1 22 333 4444 55555"
+ (lambda (out)
+ (for-each
+ (lambda (line)
+ (display line out)
+ (newline out))
+ '("1" "22" "333" "4444" "55555"))
+ #t)))
+
+(define (with-input-from-pipe cmdline proc)
+ (call-with-input-pipe cmdline
+ (lambda (port)
+ (parameterize ((current-input-port port)) (proc)))))
+
+(check-equal ("aaa" "bbb" "ccc" "ddd")
+ (with-input-from-pipe "stdwrite --stdout aaa bbb ccc ddd"
+ (lambda () (read-lines (current-input-port)))))
+
+(define (with-output-to-pipe cmdline proc)
+ (call-with-output-pipe cmdline
+ (lambda (port)
+ (parameterize ((current-output-port port)) (proc)))))
+
+(check-equal #t
+ (with-output-to-pipe "stdread 1 22 333 4444 55555"
+ (lambda ()
+ (for-each
+ (lambda (line)
+ (display line)
+ (newline))
+ '("1" "22" "333" "4444" "55555"))
+ #t)))
diff --git a/test/stdread.cpp b/test/stdread.cpp
new file mode 100644
index 0000000..fccadae
--- /dev/null
+++ b/test/stdread.cpp
@@ -0,0 +1,30 @@
+/*
+
+Read lines from standard input; each line should match one argument.
+
+*/
+
+#include <string.h>
+#include <stdio.h>
+
+int main(int argc, char * argv[])
+{
+ char s[1024];
+ int adx = 1;
+
+ for (;;)
+ {
+ if (fgets(s, sizeof(s), stdin) == 0)
+ break;
+ if (adx == argc)
+ return(1);
+ size_t sl = strlen(s);
+ if (sl > 0)
+ s[sl - 1] = 0;
+ if (strcmp(s, argv[adx]) != 0)
+ return(2);
+ adx += 1;
+ }
+
+ return(0);
+}
diff --git a/test/stdwrite.cpp b/test/stdwrite.cpp
new file mode 100644
index 0000000..dbcb818
--- /dev/null
+++ b/test/stdwrite.cpp
@@ -0,0 +1,30 @@
+/*
+
+Write lines to standard output or error; each line is an argument.
+
+*/
+
+#include <string.h>
+#include <stdio.h>
+
+int main(int argc, char * argv[])
+{
+ FILE * fp;
+
+ if (argc < 2)
+ return(1);
+ if (strcmp(argv[1], "--stdout") == 0)
+ fp = stdout;
+ else if (strcmp(argv[1], "--stderr") == 0)
+ fp = stderr;
+ else
+ return(2);
+
+ for (int adx = 2; adx < argc; adx += 1)
+ {
+ fputs(argv[adx], fp);
+ fputc('\n', fp);
+ }
+
+ return(0);
+}
diff --git a/unix/makefile b/unix/makefile
index a048ed9..a45488a 100644
--- a/unix/makefile
+++ b/unix/makefile
@@ -2,7 +2,6 @@
# Foment
#
-CC ?= gcc
CXX ?= g++
BUILD_CXX ?= g++
@@ -39,7 +38,7 @@ clean: debug release profile
-rm profile/*
.PHONY: test
-test: all foment-test stress-test chibi-test
+test: all foment-test stress-test chibi-test process-test
.PHONY: test-all
test-all: all
@@ -72,6 +71,10 @@ stress-test: all
chibi-test: all
cd ../test ; ../unix/$(TEST_BUILD)/foment $(TEST_OPTIONS) r7rs-tests.scm
+.PHONY: process-test
+process-test: all debug/stdread debug/stdwrite debug/exitcode debug/hang
+ PATH=../unix/debug:$(PATH) ; cd ../test ; ../unix/$(TEST_BUILD)/foment $(TEST_OPTIONS) runtests.scm process.scm
+
debug:
-mkdir debug
@@ -93,7 +96,7 @@ debug/foment: debug/foment.o debug/gc.o debug/syncthrd.o debug/compile.o debug/i
debug/pairs.o debug/unicode.o debug/chars.o debug/strings.o debug/vectors.o\
debug/library.o debug/execute.o debug/numbers.o debug/write.o\
debug/read.o debug/filesys.o debug/compare.o debug/main.o debug/hashtbl.o\
- debug/bignums.o debug/charset.o debug/base.o
+ debug/bignums.o debug/charset.o debug/process.o debug/base.o
$(CXX) $(LDFLAGS) -o debug/foment $^ -lpthread
release/foment: release/foment.o release/gc.o release/syncthrd.o release/compile.o release/io.o\
@@ -102,7 +105,7 @@ release/foment: release/foment.o release/gc.o release/syncthrd.o release/compile
release/vectors.o release/library.o release/execute.o release/numbers.o\
release/write.o release/read.o release/filesys.o\
release/compare.o release/main.o release/hashtbl.o release/bignums.o release/charset.o\
- release/base.o
+ release/process.o release/base.o
$(CXX) $(LDFLAGS) -o release/foment $^ -lpthread
profile/foment: profile/foment.o profile/gc.o profile/syncthrd.o profile/compile.o profile/io.o\
@@ -111,7 +114,7 @@ profile/foment: profile/foment.o profile/gc.o profile/syncthrd.o profile/compile
profile/vectors.o profile/library.o profile/execute.o profile/numbers.o\
profile/write.o profile/read.o profile/filesys.o\
profile/compare.o profile/main.o profile/hashtbl.o profile/bignums.o profile/charset.o\
- profile/base.o
+ profile/process.o profile/base.o
$(CXX) $(LDFLAGS) -pg -o profile/foment $^ -lpthread
debug/foment.o: ../src/foment.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/unicode.hpp
@@ -132,6 +135,8 @@ debug/execute.o: ../src/execute.cpp ../src/foment.hpp ../src/execute.hpp ../src/
debug/numbers.o: ../src/numbers.cpp ../src/foment.hpp ../src/unicode.hpp ../src/bignums.hpp
debug/bignums.o: ../src/bignums.cpp ../src/foment.hpp ../src/unicode.hpp ../src/bignums.hpp
debug/charset.o: ../src/charset.cpp ../src/foment.hpp ../src/unicode.hpp ../src/unicrng.hpp
+debug/process.o: ../src/process.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\
+ ../src/unicode.hpp
debug/io.o: ../src/io.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\
../src/unicode.hpp
debug/write.o: ../src/write.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\
@@ -162,6 +167,8 @@ release/execute.o: ../src/execute.cpp ../src/foment.hpp ../src/execute.hpp ../sr
release/numbers.o: ../src/numbers.cpp ../src/foment.hpp ../src/unicode.hpp ../src/bignums.hpp
release/bignums.o: ../src/bignums.cpp ../src/foment.hpp ../src/unicode.hpp ../src/bignums.hpp
release/charset.o: ../src/charset.cpp ../src/foment.hpp ../src/unicode.hpp ../src/unicrng.hpp
+release/process.o: ../src/process.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\
+ ../src/unicode.hpp
release/io.o: ../src/io.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\
../src/unicode.hpp
release/write.o: ../src/write.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\
@@ -180,9 +187,6 @@ debug/%.o: %.cpp
debug/%.o: ../src/%.cpp
$(CXX) $(CCDEBUG) -I ../src -o $@ $<
-debug/%.o: ../src/%.c
- $(CC) $(CCDEBUG) -I ../src -o $@ $<
-
debug/%.o: debug/%.cpp
$(CXX) $(CCDEBUG) -I ../src -o $@ $<
@@ -192,9 +196,6 @@ release/%.o: %.cpp
release/%.o: ../src/%.cpp
$(CXX) $(CCRELEASE) -I ../src -o $@ $<
-release/%.o: ../src/%.c
- $(CC) $(CCRELEASE) -I ../src -o $@ $<
-
release/%.o: debug/%.cpp
$(CXX) $(CCRELEASE) -I ../src -o $@ $<
@@ -204,12 +205,14 @@ profile/%.o: %.cpp
profile/%.o: ../src/%.cpp
$(CXX) $(CCPROFILE) -I ../src -o $@ $<
-profile/%.o: ../src/%.c
- $(CC) $(CCPROFILE) -I ../src -o $@ $<
-
profile/%.o: debug/%.cpp
$(CXX) $(CCPROFILE) -I ../src -o $@ $<
+debug/%: ../test/%.cpp
+ $(BUILD_CXX) $(CCDEBUG) -o debug/$*.o $<
+ $(BUILD_CXX) debug/$*.o -o $@
+
debug/txt2cpp: ../src/txt2cpp.cpp
$(BUILD_CXX) $(CCDEBUG) ../src/txt2cpp.cpp -o debug/txt2cpp.o
$(BUILD_CXX) debug/txt2cpp.o -o debug/txt2cpp
+
diff --git a/windows/makefile b/windows/makefile
index 0579f7a..e8205f9 100644
--- a/windows/makefile
+++ b/windows/makefile
@@ -2,22 +2,25 @@
# Foment
#
+PATH = $(PATH);../windows/debug
+
TEST_OPTIONS = --check-heap
TEST_BUILD = debug
-CLDEBUG = /nologo /MD /W3 /Gm /EHsc /Zi /Od /c /Fodebug/ /DFOMENT_DEBUG /DFOMENT_WINDOWS
-CLRELEASE = /nologo /MD /W3 /Ox /Zi /GA /EHsc /Forelease/ /c /DFOMENT_WINDOWS
+CLDEBUG = /nologo /MD /W3 /EHsc /Zi /Od /c /Fodebug\ /DFOMENT_DEBUG /DFOMENT_WINDOWS
+CLRELEASE = /nologo /MD /W3 /Ox /Zi /GA /EHsc /Forelease\ /c /DFOMENT_WINDOWS
LIBS = ws2_32.lib iphlpapi.lib
-all: debug release debug\foment.exe release\foment.exe
+all: debug release debug\foment.exe release\foment.exe debug\stdread.exe debug\stdwrite.exe\
+ debug\exitcode.exe debug\hang.exe
clean: debug release manual
del /Q debug\*
del /Q release\*
del /Q manual\*
-test: all foment-test stress-test chibi-test
+test: all foment-test stress-test chibi-test process-test
test-all: all test-no-collector test-mark-sweep
@@ -53,6 +56,10 @@ chibi-test: all
cd ..\test
..\windows\$(TEST_BUILD)\foment $(TEST_OPTIONS) runtests.scm stress.scm r7rs-tests.scm
+process-test: all
+ cd ..\test
+ ..\windows\$(TEST_BUILD)\foment $(TEST_OPTIONS) runtests.scm process.scm
+
debug:
-mkdir debug
@@ -74,7 +81,7 @@ debug\foment.exe: debug\foment.obj debug\gc.obj debug\syncthrd.obj debug\compile
debug\pairs.obj debug\unicode.obj debug\chars.obj debug\strings.obj debug\vectors.obj\
debug\library.obj debug\execute.obj debug\numbers.obj debug\write.obj\
debug\read.obj debug\filesys.obj debug\compare.obj debug\main.obj debug\hashtbl.obj\
- debug\bignums.obj debug\charset.obj debug\base.obj
+ debug\bignums.obj debug\charset.obj debug\process.obj debug\base.obj
link /nologo /subsystem:console /out:debug\foment.exe /debug /pdb:debug\foment.pdb\
/largeaddressaware $** $(LIBS)
@@ -84,14 +91,25 @@ release\foment.exe: release\foment.obj release\gc.obj release\syncthrd.obj relea
release\strings.obj release\vectors.obj release\library.obj release\execute.obj\
release\numbers.obj release\write.obj release\read.obj\
release\filesys.obj release\compare.obj release\main.obj release\hashtbl.obj\
- release\bignums.obj release\charset.obj release\base.obj
+ release\bignums.obj release\charset.obj release\process.obj release\base.obj
# link /nologo /subsystem:console /out:release\foment.exe $**
link /nologo /subsystem:console /out:release\foment.exe /debug /pdb:release\foment.pdb\
/largeaddressaware $** $(LIBS)
debug\txt2cpp.exe: debug\txt2cpp.obj
- link /nologo /subsystem:console /out:debug\txt2cpp.exe\
- debug\txt2cpp.obj
+ link /nologo /subsystem:console /out:debug\txt2cpp.exe debug\txt2cpp.obj
+
+debug\stdread.exe: debug\stdread.obj
+ link /nologo /subsystem:console /out:debug\stdread.exe debug\stdread.obj
+
+debug\stdwrite.exe: debug\stdwrite.obj
+ link /nologo /subsystem:console /out:debug\stdwrite.exe debug\stdwrite.obj
+
+debug\exitcode.exe: debug\exitcode.obj
+ link /nologo /subsystem:console /out:debug\exitcode.exe debug\exitcode.obj
+
+debug\hang.exe: debug\hang.obj
+ link /nologo /subsystem:console /out:debug\hang.exe debug\hang.obj
debug\foment.obj: ..\src\foment.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\unicode.hpp
debug\gc.obj: ..\src\gc.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp
@@ -111,6 +129,8 @@ debug\execute.obj: ..\src\execute.cpp ..\src\foment.hpp ..\src\execute.hpp ..\sr
debug\numbers.obj: ..\src\numbers.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\bignums.hpp
debug\bignums.obj: ..\src\bignums.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\bignums.hpp
debug\charset.obj: ..\src\charset.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\unicrng.hpp
+debug\process.obj: ..\src\process.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\
+ ..\src\unicode.hpp
debug\io.obj: ..\src\io.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp ..\src\unicode.hpp
debug\write.obj: ..\src\write.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\
..\src\compile.hpp
@@ -140,6 +160,8 @@ release\execute.obj: ..\src\execute.cpp ..\src\foment.hpp ..\src\execute.hpp ..\
release\numbers.obj: ..\src\numbers.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\bignums.hpp
release\bignums.obj: ..\src\bignums.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\bignums.hpp
release\charset.obj: ..\src\charset.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\unicrng.hpp
+release\process.obj: ..\src\process.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\
+ ..\src\unicode.hpp
release\io.obj: ..\src\io.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\
..\src\unicode.hpp
release\write.obj: ..\src\write.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\
@@ -154,6 +176,11 @@ release\base.obj: debug\base.cpp
debug\txt2cpp.obj: ..\src\txt2cpp.cpp
+debug\stdread.obj: ..\test\stdread.cpp
+debug\stdwrite.obj: ..\test\stdwrite.cpp
+debug\exitcode.obj: ..\test\exitcode.cpp
+debug\hang.obj: ..\test\hang.cpp
+
{.}.cpp.{debug}.obj:
cl $(CLDEBUG) $(*B).cpp
@@ -178,3 +205,5 @@ debug\txt2cpp.obj: ..\src\txt2cpp.cpp
{debug\}.cpp.{release}.obj:
cl $(CLRELEASE) debug\$(*B).cpp
+{..\test\}.cpp.{debug}.obj:
+ cl $(CLDEBUG) ..\test\$(*B).cpp