YAP 7.1.0
streams.c
1/*************************************************************************
2 * *
3 * YAP Prolog *
4 * *
5 * Yap Prolog was developed at NCCUP - Universidade do Porto *
6 * *
7 * Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
8 * *
9 **************************************************************************
10 * *
11 * File: iopreds.c * Last rev: 5/2/88
12 ** mods: * comments: Input/Output C implemented predicates *
13 * *
14 *************************************************************************/
15#ifdef SCCS
16static char SccsId[] = "%W% %G%";
17#endif
18
27#include "Yap.h"
28#if HAVE_FCNTL_H
29/* for O_BINARY and O_TEXT in WIN32 */
30#include <fcntl.h>
31#endif
32#include "YapEval.h"
33#include "YapHeap.h"
34#include "YapText.h"
35#include "Yatom.h"
36#include "yapio.h"
37#include <stdlib.h>
38#if HAVE_STDARG_H
39#include <stdarg.h>
40#endif
41#ifdef HAVE_UNISTD_H
42#include <unistd.h>
43#endif
44#if HAVE_CTYPE_H
45#include <ctype.h>
46#endif
47#if HAVE_WCTYPE_H
48#include <wctype.h>
49#endif
50#if HAVE_SYS_PARAM_H
51#include <sys/param.h>
52#endif
53#if HAVE_SYS_TIME_H
54#include <sys/time.h>
55#endif
56#if HAVE_SYS_TYPES_H
57#include <sys/types.h>
58#endif
59#ifdef HAVE_SYS_STAT_H
60#include <sys/stat.h>
61#endif
62#if HAVE_SYS_SELECT_H && !_MSC_VER && !defined(__MINGW32__)
63#include <sys/select.h>
64#endif
65#if HAVE_STRING_H
66#include <string.h>
67#endif
68#if HAVE_SIGNAL_H
69#include <signal.h>
70#endif
71#if HAVE_TERMIOS_H
72#include <termios.h>
73#endif
74#ifdef _WIN32
75#if HAVE_IO_H
76/* Windows */
77#include <io.h>
78#endif
79#endif
80#if !HAVE_STRNCAT
81#define strncat(X, Y, Z) strcat(X, Y)
82#endif
83#if !HAVE_STRNCPY
84#define strncpy(X, Y, Z) strcpy(X, Y)
85#endif
86#if _MSC_VER || defined(__MINGW32__)
87#if HAVE_SOCKET
88#include <winsock2.h>
89#endif
90#include <windows.h>
91#ifndef S_ISDIR
92#define S_ISDIR(x) (((x)&_S_IFDIR) == _S_IFDIR)
93#endif
94#endif
95#include "iopreds.h"
96#if HAVE_EXECINFO_H
97#include <execinfo.h>
98#endif
99
100#if _MSC_VER || defined(__MINGW32__)
101#define SYSTEM_STAT _stat
102//#include <winbase.h>
103#else
104#define SYSTEM_STAT stat
105#endif
106
107 bool IsStreamTerm(Term t) {
108 return !IsVarTerm(t) &&
109 (IsAtomTerm(t) ||
110 (IsApplTerm(t) && (FunctorOfTerm(t) == FunctorStream)));
111}
112
113
114void count_output_char(int ch, StreamDesc *s) {
115 if (ch == '\n') {
116#if MPWSHELL
117 if (mpwshell && (sno == StdOutStream || sno == StdErrStream) &&
118 !(s->status & Null_Stream_f)) {
119 putc(MPWSEP, s->file);
120 if (!(GLOBAL_Stream[LOCAL_output_stream].status & Null_Stream_f))
121 fflush(stdout);
122 }
123#endif
124 /* Inform that we have written a newline */
125 ++s->linecount;
126 ++s->charcount;
127 s->linestart = s->charcount;
128 } else {
129 ++s->charcount;
130 }
131}
132
133static void CloseStream(int sno);
134
135FILE *Yap_GetInputStream(Term t, const char *msg) {
136 int sno = Yap_CheckStream(t, Input_Stream_f, msg);
137 FILE *rc;
138
139 if (!(GLOBAL_Stream[sno].status &
140 (Tty_Stream_f | Socket_Stream_f | Pipe_Stream_f)))
141 rc = GLOBAL_Stream[sno].file;
142 else
143 rc = NULL;
144 UNLOCK(GLOBAL_Stream[sno].streamlock);
145 return rc;
146}
147
148FILE *Yap_GetOutputStream(Term t, const char *msg) {
149 int sno = Yap_CheckStream(t, Output_Stream_f, msg);
150 FILE *rc;
151
152 if (!(GLOBAL_Stream[sno].status & (Tty_Stream_f | Socket_Stream_f)))
153 rc = GLOBAL_Stream[sno].file;
154 else
155 rc = NULL;
156 UNLOCK(GLOBAL_Stream[sno].streamlock);
157 return rc;
158}
159
160int GetFreeStreamD(void) {
161 CACHE_REGS
162 LOCK(GLOBAL_StreamDescLock);
163 int sno;
164 for (sno = 0; sno < MaxStreams; ++sno) {
165 if (GLOBAL_Stream[sno].status & Free_Stream_f) {
166 break;
167 }
168 }
169#if HAVE_BACKTRACEX
170 void *callstack[256];
171 int i;
172 if (sno > cmax) {
173 cmax++;
174 for (i=7; i< sno; i++)
175 fprintf(stderr," %d %x\n", i,GLOBAL_Stream[i].status);
176 }
177 fprintf(stderr, "++++ got %d\n", sno);
178 int frames = backtrace(callstack, 256);
179 char **strs = backtrace_symbols(callstack, frames);
180 fprintf(stderr, "Execution stack:\n");
181 for (i = 0; i < 5; ++i) {
182 fprintf(stderr, " %s\n", strs[i]);
183 }
184 free(strs);
185#endif
186 if (sno == MaxStreams) {
187 UNLOCK(GLOBAL_StreamDescLock);
188 return -1;
189 }
190 LOCK(GLOBAL_Stream[sno].streamlock);
191 GLOBAL_Stream[sno].status &= ~Free_Stream_f;
192 UNLOCK(GLOBAL_StreamDescLock);
193 GLOBAL_Stream[sno].encoding = LOCAL_encoding;
194 return sno;
195}
196
197int Yap_GetFreeStreamD(void) { return GetFreeStreamD(); }
198
199int Yap_FirstFreeStreamD(void) {
200 int i;
201 for (i=3; i<MaxStreams; ++i) {
202 if (GLOBAL_Stream[i].status & Free_Stream_f) {
203 return i;
204 }
205 }
206 return MaxStreams;
207}
208
212bool Yap_clearInput(int sno) {
213 if (!(GLOBAL_Stream[sno].status & Tty_Stream_f) || sno < 3)
214 return true;
215 if (GLOBAL_Stream[sno].vfs) {
216 GLOBAL_Stream[sno].vfs->flush(sno);
217 return true;
218 }
219#if USE_READLINE
220 if (GLOBAL_Stream[sno].status & Readline_Stream_f)
221 return Yap_readline_clear_pending_input(GLOBAL_Stream + sno);
222#endif
223#if HAVE_FPURGE
224 fflush(NULL);
225 return fpurge(GLOBAL_Stream[sno].file) == 0;
226#elif HAVE_TCFLUSH
227 return tcflush(fileno(GLOBAL_Stream[sno].file), TCIOFLUSH) == 0;
228#elif MSC_VER
229 return fflush(GLOBAL_Stream[sno].file) == 0;
230#endif
231 return false;
232}
233
234bool Yap_flush(int sno) {
235 if (!(GLOBAL_Stream[sno].status & Tty_Stream_f))
236 return true;
237 if (GLOBAL_Stream[sno].vfs) {
238 GLOBAL_Stream[sno].vfs->flush(sno);
239 return true;
240 }
241 return fflush(GLOBAL_Stream[sno].file) == 0;
242}
243
244static Int clear_input(USES_REGS1) {
245 int sno =
246 Yap_CheckStream(ARG1, Input_Stream_f | Socket_Stream_f, "clear_input/1");
247 if (sno != -1)
248 UNLOCK(GLOBAL_Stream[sno].streamlock);
249 return Yap_clearInput(sno);
250}
251
252static Term lineCount(int sno) {
253 Term tout;
254 /* one has to be somewhat more careful because of terminals */
255 if (GLOBAL_Stream[sno].status & Tty_Stream_f) {
256 Int no = 1;
257 int i;
258 Atom my_stream;
259#if HAVE_SOCKET
260 if (GLOBAL_Stream[sno].status & Socket_Stream_f)
261 my_stream = AtomSocket;
262 else
263#endif
264 if (GLOBAL_Stream[sno].status & Pipe_Stream_f)
265 my_stream = AtomPipe;
266 else if (GLOBAL_Stream[sno].status & InMemory_Stream_f)
267 my_stream = AtomCharsio;
268 else
269 my_stream = GLOBAL_Stream[sno].name;
270 for (i = 0; i < MaxStreams; i++) {
271 if ((GLOBAL_Stream[i].status &
272 (Socket_Stream_f | Pipe_Stream_f | InMemory_Stream_f)) &&
273 !(GLOBAL_Stream[i].status & (Free_Stream_f)) &&
274 GLOBAL_Stream[i].name == my_stream)
275 no += GLOBAL_Stream[i].linecount - 1;
276 }
277 tout = MkIntTerm(no);
278 } else
279 tout = MkIntTerm(GLOBAL_Stream[sno].linecount);
280 UNLOCK(GLOBAL_Stream[sno].streamlock);
281 return tout;
282}
283
284static Int stream_flags(USES_REGS1) { /* '$stream_flags'(+N,-Flags) */
285 Term trm;
286 trm = Deref(ARG1);
287 if (IsVarTerm(trm) || !IsIntTerm(trm))
288 return (FALSE);
289 return (Yap_unify_constant(ARG2,
290 MkIntTerm(GLOBAL_Stream[IntOfTerm(trm)].status)));
291}
292
293static Int p_check_stream(USES_REGS1) { /* '$check_stream'(Stream,Mode) */
294 Term mode = Deref(ARG2);
295 int sno = Yap_CheckStream(
296 ARG1, AtomOfTerm(mode) == AtomRead ? Input_Stream_f : Output_Stream_f,
297 "check_stream/2");
298 if (sno != -1)
299 UNLOCK(GLOBAL_Stream[sno].streamlock);
300 return sno != -1;
301}
302
303static Int p_check_if_stream(USES_REGS1) { /* '$check_stream'(Stream) */
304 int sno = Yap_CheckStream(ARG1,
305 Input_Stream_f | Output_Stream_f | Append_Stream_f |
306 Socket_Stream_f,
307 "check_stream/1");
308 if (sno != -1)
309 UNLOCK(GLOBAL_Stream[sno].streamlock);
310 return sno != -1;
311}
312
313static Int
314is_input(int sno USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
315 bool rc = GLOBAL_Stream[sno].status & Input_Stream_f;
316 return rc;
317}
318
319static Int
320is_output(int sno USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
321 bool rc = GLOBAL_Stream[sno].status & (Output_Stream_f | Append_Stream_f);
322 return rc;
323}
324
325static Int
326has_bom(int sno, Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
327 bool rc = GLOBAL_Stream[sno].status & HAS_BOM_f;
328 if (!IsVarTerm(t2) && !booleanFlag(t2)) {
329 // Yap_Error( DOMAIN_ERROR_BOOLEAN, t2, " stream_property/2");
330 return false;
331 }
332 if (rc) {
333 return Yap_unify_constant(t2, TermTrue);
334 } else {
335 return Yap_unify_constant(t2, TermFalse);
336 }
337}
338
339static Int
340has_reposition(int sno,
341 Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
342 bool rc = GLOBAL_Stream[sno].status & Seekable_Stream_f;
343 if (!IsVarTerm(t2) && !booleanFlag(t2)) {
344 // Yap_Error( DOMAIN_ERROR_BOOLEAN, t2, " stream_property/2");
345 return false;
346 }
347 if (rc) {
348 return Yap_unify_constant(t2, TermTrue);
349 } else {
350 return Yap_unify_constant(t2, TermFalse);
351 }
352}
353
354bool Yap_SetCurInpPos(
355 int sno, Int pos USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
356
357 if (GLOBAL_Stream[sno].vfs) {
358 if (GLOBAL_Stream[sno].vfs->seek &&
359 GLOBAL_Stream[sno].vfs->seek(sno, 0L, SEEK_END) == -1) {
360 UNLOCK(GLOBAL_Stream[sno].streamlock);
361 PlIOError(SYSTEM_ERROR_INTERNAL, pos,
362 "fseek failed for set_stream_position/2: %s", strerror(errno));
363 return (FALSE);
364 }
365 } else if (fseek(GLOBAL_Stream[sno].file, pos, SEEK_SET) == -1) {
366 UNLOCK(GLOBAL_Stream[sno].streamlock);
367 PlIOError(SYSTEM_ERROR_INTERNAL, MkIntegerTerm(0),
368 "fseek failed for set_stream_position/2: %s", strerror(errno));
369 return (FALSE);
370 }
371 return true;
372}
373
374Atom Yap_guessFileName(FILE *file, int sno, size_t max) {
375 if (!file) {
376 Atom at = Yap_LookupAtom("mem");
377 return at;
378 }
379 int f = fileno(file);
380 if (f < 0) {
381 Atom at = Yap_LookupAtom("fmem");
382 return at;
383 }
384
385 int i = push_text_stack();
386#if __linux__
387 size_t maxs = Yap_Max(1023, max - 1);
388 char *path = Malloc(1024), *nameb = Malloc(maxs + 1);
389 size_t len;
390 if ((len = snprintf(path, 1023, "/proc/self/fd/%d", f)) >= 0 &&
391 (len = readlink(path, nameb, maxs)) > 0) {
392 nameb[len] = '\0';
393 Atom at = Yap_LookupAtom(nameb);
394 pop_text_stack(i);
395 return at;
396 }
397#elif __APPLE__
398 size_t maxs = Yap_Max(1023, max - 1);
399 char *nameb = Malloc(maxs + 1);
400 if (fcntl(f, F_GETPATH, nameb) != -1) {
401 Atom at = Yap_LookupAtom(nameb);
402 pop_text_stack(i);
403 return at;
404 }
405#else
406 TCHAR *path = Malloc(MAX_PATH + 1), *nameb = Malloc(MAX_PATH + 1);
407
408 if (!GetFullPathName(path, MAX_PATH, nameb, NULL)) {
409 pop_text_stack(i);
410 return NULL;
411 } else {
412 int i;
413 unsigned char *ptr = (unsigned char *)nameb;
414 for (i = 0; i < strlen(path); i++)
415 ptr += put_utf8(ptr, path[i]);
416 *ptr = '\0';
417 Atom at = Yap_LookupAtom(nameb);
418 pop_text_stack(i);
419 return at;
420 }
421#endif
422 if (!StreamName(sno)) {
423 return NULL;
424 }
425 pop_text_stack(i);
426 return AtomOfTerm(StreamName(sno));
427}
428
429static Int representation_error(int sno, Term t2 USES_REGS) {
430 stream_flags_t flags =
431 GLOBAL_Stream[sno].status & (RepError_Xml_f | RepError_Prolog_f);
432 /* '$representation_error'(+Stream,-ErrorMessage) */
433 if (!IsVarTerm(t2) && isatom(t2)) {
434 return false;
435 }
436 if (flags & RepError_Xml_f) {
437 return Yap_unify(t2, TermXml);
438 }
439 if (flags & RepError_Prolog_f) {
440 return Yap_unify(t2, TermProlog);
441 }
442 return Yap_unify(t2, TermError);
443}
444
445static Int file_name(int sno, Term t2 USES_REGS) {
446 return Yap_unify_constant(t2, MkAtomTerm(GLOBAL_Stream[sno].name));
447}
448
449static Int file_no(int sno, Term t2 USES_REGS) {
450 int f = Yap_GetStreamFd(sno);
451 Term rc = MkIntTerm(f);
452 if (!IsVarTerm(t2) && !IsIntTerm(t2)) {
453 return false;
454 }
455 return Yap_unify_constant(t2, rc);
456}
457
458static bool SetCloseOnAbort(int sno, bool close) {
459 if (close) {
460 GLOBAL_Stream[sno].status |= DoNotCloseOnAbort_Stream_f;
461 } else {
462 GLOBAL_Stream[sno].status &= ~DoNotCloseOnAbort_Stream_f;
463 }
464 return true;
465}
466
467static Int has_close_on_abort(
468 int sno, Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
469 bool rc = GLOBAL_Stream[sno].status & DoNotCloseOnAbort_Stream_f;
470 if (!IsVarTerm(t2)) {
471 return t2 == (rc ? TermTrue : TermFalse);
472 }
473 if (rc) {
474 return Yap_unify_constant(t2, TermTrue);
475 } else {
476 return Yap_unify_constant(t2, TermFalse);
477 }
478}
479
480static bool
481has_encoding(int sno,
482 Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
483 const char *s = enc_name(GLOBAL_Stream[sno].encoding);
484 return Yap_unify(t2, MkAtomTerm(Yap_LookupAtom(s)));
485}
486
487static bool
488found_eof(int sno,
489 Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
490 stream_flags_t flags =
491 GLOBAL_Stream[sno].status & (Past_Eof_Stream_f | Eof_Stream_f);
492 if (!IsVarTerm(t2) && !(isatom(t2))) {
493 return FALSE;
494 }
495 if (flags & Past_Eof_Stream_f)
496 return Yap_unify(t2, MkAtomTerm(AtomPast));
497 if (flags & Eof_Stream_f)
498 return Yap_unify(t2, MkAtomTerm(AtomAt));
499 return Yap_unify(t2, MkAtomTerm(AtomAltNot));
500}
501
502static bool stream_mode(int sno, Term t2 USES_REGS) {
503 /* '$set_output'(+Stream,-ErrorMessage) */
504 stream_flags_t flags = GLOBAL_Stream[sno].status;
505 if (!IsVarTerm(t2) && !(isatom(t2))) {
506 return false;
507 }
508 if (flags & Input_Stream_f)
509 return Yap_unify(t2, TermRead);
510 if (flags & Append_Stream_f)
511 return Yap_unify(t2, TermWrite);
512 if (flags & Output_Stream_f)
513 return Yap_unify(t2, TermWrite);
514 return false;
515}
516
517static bool
518stream_tty(int sno,
519 Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
520 stream_flags_t flags = GLOBAL_Stream[sno].status & (Tty_Stream_f);
521 if (!IsVarTerm(t2) && !(isatom(t2))) {
522 return FALSE;
523 }
524 if (flags & Tty_Stream_f)
525 return Yap_unify(t2, TermTrue);
526 return Yap_unify(t2, TermFalse);
527}
528
529static bool
530stream_type(int sno,
531 Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
532 stream_flags_t flags = GLOBAL_Stream[sno].status & (Binary_Stream_f);
533 if (!IsVarTerm(t2) && !(isatom(t2))) {
534 return FALSE;
535 }
536 if (flags & Binary_Stream_f)
537 return Yap_unify(t2, TermBinary);
538 return Yap_unify(t2, TermText);
539}
540
541static bool
542stream_position(int sno,
543 Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
544 Term tout = StreamPosition(sno);
545 return Yap_unify(t2, tout);
546}
547
548static bool stream_line_count(
549 int sno, Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
550 Term tout = lineCount(sno);
551 return Yap_unify(t2, tout);
552}
553
554static bool stream_line_number(
555 int sno, Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
556 Term tout = MkIntegerTerm(GLOBAL_Stream[sno].linecount);
557 return Yap_unify(t2, tout);
558}
559
560static bool
561SetBuffering(int sno, Atom at) { /* '$set_bufferingt'(+Stream,-ErrorMessage) */
562 if (at == AtomFull) {
563 if (setvbuf(GLOBAL_Stream[sno].file, NULL, _IOFBF, 0) < 0)
564 return PlIOError(SYSTEM_ERROR_INTERNAL, Yap_MkStream(sno),
565 "could not set buffer");
566 } else if (at == AtomLine) {
567 if (setvbuf(GLOBAL_Stream[sno].file, NULL, _IOLBF, 0) < 0)
568 return PlIOError(SYSTEM_ERROR_INTERNAL, Yap_MkStream(sno),
569 "could not set line buffering");
570 } else if (at == AtomFalse) {
571 if (setvbuf(GLOBAL_Stream[sno].file, NULL, _IONBF, 0) < 0)
572 return PlIOError(SYSTEM_ERROR_INTERNAL, Yap_MkStream(sno),
573 "could not set disable buffering");
574 } else {
575 CACHE_REGS
576 LOCAL_Error_TYPE = DOMAIN_ERROR_OUT_OF_RANGE;
577 LOCAL_ErrorMessage = "in set_stream/2:buffer";
578 return false;
579 }
580 return true;
581}
582
583static bool SetBuffer(int sno,
584 Int sz) { /* '$set_bufferingt'(+Stream,-ErrorMessage) */
585 if (setvbuf(GLOBAL_Stream[sno].file, NULL, _IOFBF, sz) < 0) {
586 return PlIOError(SYSTEM_ERROR_INTERNAL, Yap_MkStream(sno),
587 "could not set buffer");
588 }
589 return true;
590}
591
592static bool
593eof_action(int sno,
594 Term t2 USES_REGS) { /* '$set_output'(+Stream,-ErrorMessage) */
595 stream_flags_t flags =
596 GLOBAL_Stream[sno].status &
597 (Eof_Error_Stream_f | Reset_Eof_Stream_f | Push_Eof_Stream_f);
598 if (!IsVarTerm(t2) && !(isatom(t2))) {
599 return FALSE;
600 }
601 if (flags & Eof_Error_Stream_f) {
602 return Yap_unify(t2, TermError);
603 }
604 if (flags & Reset_Eof_Stream_f) {
605 return Yap_unify(t2, TermReset);
606 }
607 return Yap_unify(t2, TermEOfCode);
608}
609
610#define STREAM_PROPERTY_DEFS() \
611 PAR("alias", filler, STREAM_PROPERTY_ALIAS) \
612 , PAR("bom", filler, STREAM_PROPERTY_BOM), \
613 PAR("close_on_abort", filler, STREAM_PROPERTY_CLOSE_ON_ABORT), \
614 PAR("encoding", filler, STREAM_PROPERTY_ENCODING), \
615 PAR("end_of_stream", filler, STREAM_PROPERTY_END_OF_STREAM), \
616 PAR("eof_action", filler, STREAM_PROPERTY_EOF_ACTION), \
617 PAR("file_name", filler, STREAM_PROPERTY_FILE_NAME), \
618 PAR("file_no", filler, STREAM_PROPERTY_FILE_NO), \
619 PAR("input", ok, STREAM_PROPERTY_INPUT), \
620 PAR("line_count", ok, STREAM_PROPERTY_LINE_COUNT), \
621 PAR("line_number", ok, STREAM_PROPERTY_LINE_NUMBER), \
622 PAR("mode", filler, STREAM_PROPERTY_MODE), \
623 PAR("output", filler, STREAM_PROPERTY_OUTPUT), \
624 PAR("position", isatom, STREAM_PROPERTY_POSITION), \
625 PAR("reposition", filler, STREAM_PROPERTY_REPOSITION), \
626 PAR("representation_errors", filler, \
627 STREAM_PROPERTY_REPRESENTATION_ERRORS), \
628 PAR("type", filler, STREAM_PROPERTY_TYPE), \
629 PAR("tty", filler, STREAM_PROPERTY_TTY), \
630 PAR(NULL, ok, STREAM_PROPERTY_END)
631
632#define PAR(x, y, z) z
633
634typedef enum stream_property_enum_choices {
635 STREAM_PROPERTY_DEFS()
636} stream_property_choices_t;
637
638#undef PAR
639
640#define PAR(x, y, z) \
641 { x, y, z }
642
643static const param_t stream_property_defs[] = {STREAM_PROPERTY_DEFS()};
644#undef PAR
645
646static bool do_stream_property(int sno,
647 xarg *args USES_REGS) { /* Init current_stream */
648 stream_property_choices_t i;
649 bool rc = true;
650
651 for (i = 0; i < STREAM_PROPERTY_END; i++) {
652 if (args[i].used) {
653 switch (i) {
654 case STREAM_PROPERTY_ALIAS: {
655 Term ta = args[STREAM_PROPERTY_ALIAS].tvalue;
656 rc = rc & Yap_FetchStreamAlias(sno, ta PASS_REGS);
657 } break;
658 case STREAM_PROPERTY_BOM:
659 rc = rc && has_bom(sno, args[STREAM_PROPERTY_BOM].tvalue PASS_REGS);
660 break;
661 case STREAM_PROPERTY_CLOSE_ON_ABORT:
662 rc = rc &&
663 has_close_on_abort(
664 sno, args[STREAM_PROPERTY_CLOSE_ON_ABORT].tvalue PASS_REGS);
665 break;
666 case STREAM_PROPERTY_ENCODING:
667 rc = rc &&
668 has_encoding(sno, args[STREAM_PROPERTY_ENCODING].tvalue PASS_REGS);
669 break;
670 case STREAM_PROPERTY_END_OF_STREAM:
671 rc = rc &&
672 found_eof(sno,
673 args[STREAM_PROPERTY_END_OF_STREAM].tvalue PASS_REGS);
674 break;
675 case STREAM_PROPERTY_EOF_ACTION:
676 rc = rc &&
677 eof_action(sno, args[STREAM_PROPERTY_EOF_ACTION].tvalue PASS_REGS);
678 break;
679 case STREAM_PROPERTY_FILE_NAME:
680 rc = rc &&
681 file_name(sno, args[STREAM_PROPERTY_FILE_NAME].tvalue PASS_REGS);
682 break;
683 case STREAM_PROPERTY_FILE_NO:
684 rc = rc && file_no(sno, args[STREAM_PROPERTY_FILE_NO].tvalue PASS_REGS);
685 break;
686 case STREAM_PROPERTY_INPUT:
687 rc = rc && is_input(sno PASS_REGS);
688 break;
689 case STREAM_PROPERTY_LINE_NUMBER:
690 rc = rc && stream_line_number(
691 sno, args[STREAM_PROPERTY_LINE_NUMBER].tvalue PASS_REGS);
692 break;
693 case STREAM_PROPERTY_LINE_COUNT:
694 rc = rc && stream_line_count(
695 sno, args[STREAM_PROPERTY_LINE_COUNT].tvalue PASS_REGS);
696 break;
697 case STREAM_PROPERTY_MODE:
698 rc =
699 rc && stream_mode(sno, args[STREAM_PROPERTY_MODE].tvalue PASS_REGS);
700 break;
701 case STREAM_PROPERTY_OUTPUT:
702 rc = rc && is_output(sno PASS_REGS);
703 break;
704 case STREAM_PROPERTY_POSITION:
705 rc = rc && stream_position(
706 sno, args[STREAM_PROPERTY_POSITION].tvalue PASS_REGS);
707 break;
708 case STREAM_PROPERTY_REPOSITION:
709 rc = rc && has_reposition(
710 sno, args[STREAM_PROPERTY_REPOSITION].tvalue PASS_REGS);
711 break;
712 case STREAM_PROPERTY_REPRESENTATION_ERRORS:
713 rc = rc &&
714 representation_error(
715 sno,
716 args[STREAM_PROPERTY_REPRESENTATION_ERRORS].tvalue PASS_REGS);
717 break;
718 case STREAM_PROPERTY_TYPE:
719 rc =
720 rc && stream_type(sno, args[STREAM_PROPERTY_TYPE].tvalue PASS_REGS);
721 break;
722 case STREAM_PROPERTY_TTY:
723 rc = rc && stream_tty(sno, args[STREAM_PROPERTY_TTY].tvalue PASS_REGS);
724 break;
725 case STREAM_PROPERTY_END:
726 default:
727 Yap_ThrowError(DOMAIN_ERROR_SET_STREAM_OPTION, ARG1, "bad option to stream_property/2" );
728 rc = false;
729 break;
730 }
731 }
732 }
733 return rc;
734}
735
736static xarg *generate_property(int sno, Term t2,
737 stream_property_choices_t p USES_REGS) {
738 if (p == STREAM_PROPERTY_INPUT)
739 Yap_unify(t2, MkAtomTerm(AtomInput));
740 else if (p == STREAM_PROPERTY_OUTPUT)
741 Yap_unify(t2, MkAtomTerm(AtomOutput));
742 else {
743 Functor f = Yap_MkFunctor(Yap_LookupAtom(stream_property_defs[p].name), 1);
744 Yap_unify(t2, Yap_MkNewApplTerm(f, 1));
745 }
746 return Yap_ArgListToVector(t2, stream_property_defs, STREAM_PROPERTY_END,
747 NULL,DOMAIN_ERROR_STREAM_PROPERTY_OPTION);
748}
749
750static Int cont_stream_property(USES_REGS1) { /* current_stream */
751 bool det = false;
752 xarg *args;
753 int i = IntOfTerm(EXTRA_CBACK_ARG(2, 1));
754 stream_property_choices_t p = STREAM_PROPERTY_END;
755 bool rc;
756 Term t2 = Deref(ARG2);
757 Term t1 = Deref(ARG1);
758
759 if (IsVarTerm(t2)) {
760 p = IntOfTerm(EXTRA_CBACK_ARG(2, 2));
761 args = generate_property(i, t2, p++ PASS_REGS);
762
763 EXTRA_CBACK_ARG(2, 2) = MkIntTerm(p % STREAM_PROPERTY_END);
764 // otherwise, just drop through
765 } else {
766 args = Yap_ArgListToVector(t2, stream_property_defs, STREAM_PROPERTY_END,NULL,
767 DOMAIN_ERROR_STREAM_PROPERTY_OPTION);
768 }
769 if (args == NULL) {
770 if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
771 if (LOCAL_Error_TYPE == DOMAIN_ERROR_GENERIC_ARGUMENT)
772 LOCAL_Error_TYPE = DOMAIN_ERROR_STREAM_PROPERTY_OPTION;
773 Yap_ThrowError(LOCAL_Error_TYPE, t2, NULL);
774 return false;
775 }
776 cut_fail();
777 }
778 LOCK(GLOBAL_Stream[i].streamlock);
779 if (IsAtomTerm(args[STREAM_PROPERTY_ALIAS].tvalue)) {
780 // one solution only
781 i = Yap_CheckAlias(AtomOfTerm(args[STREAM_PROPERTY_ALIAS].tvalue));
782 UNLOCK(GLOBAL_Stream[i].streamlock);
783 free(args);
784 if (i < 0 || !Yap_unify(ARG1, Yap_MkStream(i))) {
785 cut_fail();
786 } else {
787 cut_succeed();
788 }
789 }
790 LOCK(GLOBAL_Stream[i].streamlock);
791 rc = do_stream_property(i, args PASS_REGS);
792 UNLOCK(GLOBAL_Stream[i].streamlock);
793 if (!det && IsVarTerm(t1)) {
794 if (rc)
795 rc = Yap_unify(ARG1, Yap_MkStream(i));
796 if (p == STREAM_PROPERTY_END) {
797 // move to next existing stream
798 LOCK(GLOBAL_StreamDescLock);
799 while (++i < MaxStreams && GLOBAL_Stream[i].status & Free_Stream_f) {
800 }
801 UNLOCK(GLOBAL_StreamDescLock);
802 if (i < MaxStreams) {
803 EXTRA_CBACK_ARG(2, 1) = MkIntTerm(i);
804 det = false;
805 } else {
806 det = true;
807 }
808 } else {
809 det = false;
810 }
811 } else {
812 // done
813 det = det || (p == STREAM_PROPERTY_END);
814 }
815 free(args);
816 if (rc) {
817 if (det)
818 cut_succeed();
819 else {
820 return true;
821 }
822 } else if (det) {
823 cut_fail();
824 } else {
825 return false;
826 }
827}
828
829static Int stream_property(USES_REGS1) { /* Init current_stream */
830 Term t1 = Deref(ARG1);
831 Term t2 = Deref(ARG2);
832 // Yap_DebugPlWrite(ARG1);fprintf(stderr,", ");
833 // Yap_DebugPlWrite(ARG2);fprintf(stderr,"\n");
834
835 /* make valgrind happy by always filling in memory */
836 EXTRA_CBACK_ARG(2, 1) = MkIntTerm(0);
837 EXTRA_CBACK_ARG(2, 2) = MkIntTerm(0);
838 if (!IsVarTerm(t1)) {
839 Int i;
840 xarg *args;
841
842 i = Yap_CheckStream(t1, Input_Stream_f | Output_Stream_f | Append_Stream_f,
843 "current_stream/3");
844 if (i < 0) {
845 UNLOCK(GLOBAL_Stream[i].streamlock);
846 if (IsAtomTerm(t1))
847 Yap_ThrowError(DOMAIN_ERROR_STREAM, t1, "bad stream descriptor");
848 else
849 Yap_ThrowError(DOMAIN_ERROR_STREAM, t1, "bad stream descriptor");
850 return false; // error...
851 }
852 EXTRA_CBACK_ARG(2, 1) = MkIntTerm(i);
853 if (IsVarTerm(t2)) {
854 return cont_stream_property(PASS_REGS1);
855 }
856 args = Yap_ArgListToVector(Deref(ARG2), stream_property_defs,
857 STREAM_PROPERTY_END,
858 NULL,
859 DOMAIN_ERROR_STREAM_PROPERTY_OPTION);
860 if (args == NULL) {
861 if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
862 if (LOCAL_Error_TYPE == DOMAIN_ERROR_PROLOG_FLAG)
863 LOCAL_Error_TYPE = DOMAIN_ERROR_STREAM_PROPERTY_OPTION;
864 Yap_Error(LOCAL_Error_TYPE, ARG2, NULL);
865 return false;
866 }
867 UNLOCK(GLOBAL_Stream[i].streamlock);
868 cut_fail();
869 }
870 if (do_stream_property(i, args PASS_REGS)) {
871 UNLOCK(GLOBAL_Stream[i].streamlock);
872 free(args);
873 cut_succeed();
874 } else {
875 UNLOCK(GLOBAL_Stream[i].streamlock);
876 free(args);
877 cut_fail();
878 }
879 } else {
880 return cont_stream_property(PASS_REGS1);
881 }
882}
883
884#define SET_STREAM_DEFS() \
885 PAR("alias", isatom, SET_STREAM_ALIAS) \
886 , PAR("buffer", booleanFlag, SET_STREAM_BUFFER), \
887 PAR("buffer_size", nat, SET_STREAM_BUFFER_SIZE), \
888 PAR("close_on_abort", booleanFlag, SET_STREAM_CLOSE_ON_ABORT), \
889 PAR("encoding", isatom, SET_STREAM_ENCODING), \
890 PAR("eof_action", isatom, SET_STREAM_EOF_ACTION), \
891 PAR("file_name", isatom, SET_STREAM_FILE_NAME), \
892 PAR("line_position", nat, SET_STREAM_LINE_POSITION), \
893 PAR("newline", filler, SET_STREAM_NEWLINE), \
894 PAR("record_position", isatom, SET_STREAM_RECORD_POSITION), \
895 PAR("representation_errors", isatom, SET_STREAM_REPRESENTATION_ERRORS), \
896 PAR("type", isatom, SET_STREAM_TYPE), \
897 PAR("tty", filler, SET_STREAM_TTY), PAR(NULL, ok, SET_STREAM_END)
898
899#define PAR(x, y, z) z
900
901typedef enum set_stream_enum_choices {
902 SET_STREAM_DEFS()
903} set_stream_enum_choices_t;
904
905#undef PAR
906
907#define PAR(x, y, z) \
908 { x, y, z }
909
910static const param_t set_stream_defs[] = {SET_STREAM_DEFS()};
911#undef PAR
912
913static bool do_set_stream(int sno,
914 Term opts USES_REGS) { /* Init current_stream */
915 xarg *args;
916 set_stream_enum_choices_t i;
917 bool rc = true;
918
919 args = Yap_ArgListToVector(opts, set_stream_defs, SET_STREAM_END,
920 NULL,DOMAIN_ERROR_SET_STREAM_OPTION);
921 if (args == NULL) {
922 if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
923 if (LOCAL_Error_TYPE == DOMAIN_ERROR_GENERIC_ARGUMENT)
924 LOCAL_Error_TYPE = DOMAIN_ERROR_SET_STREAM_OPTION;
925 Yap_Error(LOCAL_Error_TYPE, opts, NULL);
926 }
927 UNLOCK(GLOBAL_Stream[sno].streamlock);
928 return false;
929 }
930 for (i = 0; i < SET_STREAM_END; i++) {
931 if (args[i].used) {
932 Term t = args[i].tvalue;
933 switch (i) {
934 case SET_STREAM_ALIAS:
935 rc = rc && Yap_AddAlias(AtomOfTerm(t), sno);
936 break;
937 case SET_STREAM_BUFFER:
938 rc = rc && SetBuffering(sno, AtomOfTerm(t));
939 break;
940 case SET_STREAM_BUFFER_SIZE:
941 rc = rc && SetBuffer(sno, IntegerOfTerm(t));
942 break;
943 case SET_STREAM_CLOSE_ON_ABORT:
944 rc = rc &&
945 SetCloseOnAbort(
946 sno, (args[SET_STREAM_CLOSE_ON_ABORT].tvalue == TermTrue));
947 break;
948 case SET_STREAM_ENCODING: {
949 Term t2 = args[SET_STREAM_ENCODING].tvalue;
950 Atom atEnc = AtomOfTerm(t2);
951 GLOBAL_Stream[sno].encoding =
952 enc_id(atEnc->StrOfAE, (GLOBAL_Stream[sno].status & HAS_BOM_f
953 ? GLOBAL_Stream[sno].encoding
954 : ENC_OCTET));
955 Yap_DefaultStreamOps(GLOBAL_Stream + sno);
956 } break;
957 case SET_STREAM_EOF_ACTION: {
958 Term t2 = args[SET_STREAM_EOF_ACTION].tvalue;
959 if (t2 == TermError) {
960 GLOBAL_Stream[sno].status |= Eof_Error_Stream_f;
961 GLOBAL_Stream[sno].status &= ~Reset_Eof_Stream_f;
962 } else if (t2 == TermReset) {
963 GLOBAL_Stream[sno].status &= ~Eof_Error_Stream_f;
964 GLOBAL_Stream[sno].status |= Reset_Eof_Stream_f;
965 } else if (t2 == TermEOfCode) {
966 GLOBAL_Stream[sno].status &= ~Eof_Error_Stream_f;
967 GLOBAL_Stream[sno].status &= ~Reset_Eof_Stream_f;
968 } else {
969 LOCAL_Error_TYPE = DOMAIN_ERROR_OUT_OF_RANGE;
970 LOCAL_ErrorMessage = "in set_stream/2:eof_action";
971 rc = false;
972 }
973 break;
974 case SET_STREAM_FILE_NAME:
975 GLOBAL_Stream[sno].user_name = args[SET_STREAM_FILE_NAME].tvalue;
976 break;
977 case SET_STREAM_LINE_POSITION:
978 GLOBAL_Stream[sno].charcount =
979 GLOBAL_Stream[sno].linestart +
980 IntegerOfTerm(args[SET_STREAM_FILE_NAME].tvalue);
981 break;
982 case SET_STREAM_NEWLINE:
983 printf("not yet\n");
984 break;
985 case SET_STREAM_RECORD_POSITION:
986 if (args[SET_STREAM_RECORD_POSITION].tvalue == TermTrue)
987 GLOBAL_Stream[sno].status |= Seekable_Stream_f;
988 else
989 GLOBAL_Stream[sno].status &= ~Seekable_Stream_f;
990 break;
991 case SET_STREAM_REPRESENTATION_ERRORS: {
992 Term t2 = args[SET_STREAM_EOF_ACTION].tvalue;
993 if (t2 == TermXml) {
994 GLOBAL_Stream[sno].status |= RepError_Xml_f;
995 GLOBAL_Stream[sno].status &= ~RepError_Prolog_f;
996 } else if (t2 == TermError) {
997 GLOBAL_Stream[sno].status &= ~RepError_Xml_f;
998 GLOBAL_Stream[sno].status |= RepError_Prolog_f;
999 } else if (t2 == TermEOfCode) {
1000 GLOBAL_Stream[sno].status &= ~RepError_Xml_f;
1001 GLOBAL_Stream[sno].status |= RepError_Prolog_f;
1002 } else {
1003 LOCAL_Error_TYPE = DOMAIN_ERROR_OUT_OF_RANGE;
1004 LOCAL_ErrorMessage = "in set_stream/2:eof_action";
1005 rc = false;
1006 }
1007 } break;
1008 case SET_STREAM_TYPE:
1009 rc &= stream_type(sno, args[SET_STREAM_TYPE].tvalue PASS_REGS);
1010 break;
1011 case SET_STREAM_TTY:
1012 rc &= stream_tty(sno, args[SET_STREAM_TTY].tvalue PASS_REGS);
1013 break;
1014 case SET_STREAM_END:
1015 rc = false;
1016 break;
1017 }
1018 }
1019 }
1020 }
1021 UNLOCK(GLOBAL_Stream[sno].streamlock);
1022 return rc;
1023}
1024
1025static Int set_stream(USES_REGS1) { /* Init current_stream */
1026 int sno =
1027 Yap_CheckStream(ARG1, Input_Stream_f | Output_Stream_f | Append_Stream_f,
1028 "set_stream_position/2");
1029 if (sno < 0) {
1030 return false;
1031 }
1032 return do_set_stream(sno, Deref(ARG2) PASS_REGS);
1033}
1034
1039void Yap_CloseStreams(void) {
1040 CACHE_REGS
1041 int sno;
1042 fflush(NULL);
1043 for (sno = 3; sno < MaxStreams; ++sno) {
1044 if (GLOBAL_Stream[sno].status & Free_Stream_f)
1045 continue;
1046 CloseStream(sno);
1047 }
1048}
1049
1055void Yap_CloseTemporaryStreams(int min) {
1056 CACHE_REGS
1057 int sno;
1058 fflush(NULL);
1059 for (sno = min+1; sno < MaxStreams; ++sno) {
1060 if (GLOBAL_Stream[sno].status & Free_Stream_f)
1061 continue;
1062 if (GLOBAL_Stream[sno].status & (CloseOnException_Stream_f))
1063 CloseStream(sno);
1064 }
1065}
1066
1067static void CloseStream(int sno) {
1068 CACHE_REGS
1069
1070 // fflush(NULL);
1071 // __android_log_print(ANDROID_LOG_INFO, "YAPDroid", "close stream <%d>",
1072 // sno);
1073 VFS_t *me;
1074 // fprintf( stderr, "- %d\n",sno);
1075 if (sno < 3)
1076 return;
1077 if ((me = GLOBAL_Stream[sno].vfs) != NULL &&
1078 GLOBAL_Stream[sno].file == NULL) {
1079 if (me->close) {
1080 me->close(sno);
1081 }
1082 GLOBAL_Stream[sno].vfs = NULL;
1083 } else if (GLOBAL_Stream[sno].file &&
1084 (GLOBAL_Stream[sno].status & Popen_Stream_f)) {
1085 pclose(GLOBAL_Stream[sno].file);
1086 } else if (GLOBAL_Stream[sno].file &&
1087 !(GLOBAL_Stream[sno].status & (Null_Stream_f | Socket_Stream_f |
1088 InMemory_Stream_f | Pipe_Stream_f)))
1089 fclose(GLOBAL_Stream[sno].file);
1090#if HAVE_SOCKET
1091 else if (GLOBAL_Stream[sno].status & (Socket_Stream_f)) {
1092 Yap_CloseSocket(GLOBAL_Stream[sno].u.socket.fd,
1093 GLOBAL_Stream[sno].u.socket.flags,
1094 GLOBAL_Stream[sno].u.socket.domain);
1095 }
1096#endif
1097 else if (GLOBAL_Stream[sno].status & Pipe_Stream_f) {
1098 close(GLOBAL_Stream[sno].u.pipe.fd);
1099 } else if (GLOBAL_Stream[sno].status & (InMemory_Stream_f)) {
1100 Yap_CloseMemoryStream(sno);
1101 }
1102 Yap_ReleaseStream(sno);
1103 // __android_log_print(ANDROID_LOG_INFO, "YAPDroid", "close stream <%d>",
1104 // sno);
1105
1106 /* if (st->status == Socket_Stream_f|Input_Stream_f|Output_Stream_f) {
1107 Yap_CloseSocket();
1108 }
1109 */
1110}
1111
1112void Yap_CloseStream__(const char *file,
1113 const char *function, int lineno,
1114 int sno) {
1115// fprintf(stderr,"- %d: %s/%s:%d\n", sno, file, function, lineno);
1116CloseStream(sno); }
1117
1118void Yap_ReleaseStream(int sno) {
1119 CACHE_REGS
1120 GLOBAL_Stream[sno].status = Free_Stream_f;
1121 GLOBAL_Stream[sno].user_name = 0;
1122
1123 GLOBAL_Stream[sno].vfs = NULL;
1124 GLOBAL_Stream[sno].file = NULL;
1126 if (LOCAL_c_input_stream == sno) {
1127 LOCAL_c_input_stream = Yap_FindStreamForAlias(AtomUserIn);
1128 }
1129 if (LOCAL_c_output_stream == sno) {
1130 LOCAL_c_output_stream = Yap_FindStreamForAlias(AtomUserOut);
1131 }
1132 if (LOCAL_c_error_stream == sno) {
1133 LOCAL_c_error_stream = Yap_FindStreamForAlias(AtomUserErr);
1134 }
1135 /* if (st->status == Socket_Stream_f|Input_Stream_f|Output_Stream_f) {
1136 Yap_CloseSocket();
1137 }
1138 */
1139}
1140
1149static Int current_input(USES_REGS1) { /* current_input(?Stream) */
1150 Term t1 = Deref(ARG1);
1151 if (IsVarTerm(t1)) {
1152 Term t = Yap_MkStream(LOCAL_c_input_stream);
1153 YapBind(VarOfTerm(t1), t);
1154 return TRUE;
1155 } else if (!IsApplTerm(t1) || FunctorOfTerm(t1) != FunctorStream ||
1156 !IsIntTerm((t1 = ArgOfTerm(1, t1)))) {
1157 Yap_Error(DOMAIN_ERROR_STREAM, t1, "current_input/1");
1158 return FALSE;
1159 } else {
1160 return LOCAL_c_input_stream == IntOfTerm(t1);
1161 }
1162}
1163
1164bool Yap_SetInputStream(Term sd) {
1165 int sno = Yap_CheckStream(sd, Input_Stream_f, "set_input/1");
1166 if (sno < 0)
1167 return false;
1168 UNLOCK(GLOBAL_Stream[sno].streamlock);
1169 Yap_SetAlias(AtomUserIn, sno);
1170 return true;
1171}
1172
1181static Int set_input(USES_REGS1) { /* '$show_stream_position'(+Stream,Pos) */
1182 return Yap_SetInputStream(ARG1);
1183}
1184
1193static Int current_output(USES_REGS1) { /* current_output(?Stream) */
1194 Term t1 = Deref(ARG1);
1195 if (IsVarTerm(t1)) {
1196 Term t = Yap_MkStream(LOCAL_c_output_stream);
1197 YapBind(VarOfTerm(t1), t);
1198 return TRUE;
1199 } else if (!IsApplTerm(t1) || FunctorOfTerm(t1) != FunctorStream ||
1200 !IsIntTerm((t1 = ArgOfTerm(1, t1)))) {
1201 Yap_Error(DOMAIN_ERROR_STREAM, t1, "current_output/1");
1202 return FALSE;
1203 } else {
1204 return (LOCAL_c_output_stream == IntOfTerm(t1));
1205 }
1206}
1207
1216static Int current_error(USES_REGS1) { /* current_error(?Stream) */
1217 Term t1 = Deref(ARG1);
1218 if (IsVarTerm(t1)) {
1219 Term t = Yap_MkStream(LOCAL_c_error_stream);
1220 YapBind(VarOfTerm(t1), t);
1221 return TRUE;
1222 } else if (!IsApplTerm(t1) || FunctorOfTerm(t1) != FunctorStream ||
1223 !IsIntTerm((t1 = ArgOfTerm(1, t1)))) {
1224 Yap_Error(DOMAIN_ERROR_STREAM, t1, "current_error/1");
1225 return FALSE;
1226 } else {
1227 return (LOCAL_c_error_stream == IntOfTerm(t1));
1228 }
1229}
1230
1231bool Yap_SetOutputStream(Term sd) {
1232 int sno =
1233 Yap_CheckStream(sd, Output_Stream_f | Append_Stream_f, "set_output/2");
1234 if (sno < 0)
1235 return false;
1236 UNLOCK(GLOBAL_Stream[sno].streamlock);
1237 Yap_SetAlias(AtomUserOut, sno);
1238 return true;
1239}
1240
1241bool Yap_SetErrorStream(Term sd) {
1242 int sno =
1243 Yap_CheckStream(sd, Output_Stream_f | Append_Stream_f, "set_error/2");
1244 if (sno < 0)
1245 return false;
1246 LOCAL_c_error_stream = sno;
1247 UNLOCK(GLOBAL_Stream[sno].streamlock);
1248 Yap_SetAlias(AtomUserErr, sno);
1249 return true;
1250}
1251
1260static Int set_error(USES_REGS1) { /* '$show_stream_position'(+Stream,Pos) */
1261 return Yap_SetErrorStream(ARG1);
1262}
1263
1274static Int set_output(USES_REGS1) { /* '$show_stream_position'(+Stream,Pos) */
1275 return Yap_SetOutputStream(Deref(ARG1));
1276}
1277
1278static Int p_user_file_name(USES_REGS1) {
1279 Term tout;
1280 int sno =
1281 Yap_CheckStream(Deref(ARG1), Input_Stream_f | Output_Stream_f | Append_Stream_f,
1282 "user_file_name/2");
1283 if (sno < 0)
1284 return (FALSE);
1285 tout = Yap_StreamUserName(sno);
1286 UNLOCK(GLOBAL_Stream[sno].streamlock);
1287 return (Yap_unify_constant(ARG2, tout));
1288}
1289
1290static Int p_file_name(USES_REGS1) {
1291 Term tout;
1292 int sno = Yap_CheckStream(
1293 Deref(ARG1), Input_Stream_f | Output_Stream_f | Append_Stream_f, "file_name/2");
1294 if (sno < 0)
1295 return (FALSE);
1296#if HAVE_SOCKET
1297 if (GLOBAL_Stream[sno].status & Socket_Stream_f)
1298 tout = MkAtomTerm(AtomSocket);
1299 else
1300#endif
1301 if (GLOBAL_Stream[sno].status & Pipe_Stream_f)
1302 tout = MkAtomTerm(AtomPipe);
1303 else if (GLOBAL_Stream[sno].status & InMemory_Stream_f)
1304 tout = MkAtomTerm(AtomCharsio);
1305 else
1306 tout = MkAtomTerm(GLOBAL_Stream[sno].name);
1307 UNLOCK(GLOBAL_Stream[sno].streamlock);
1308 return Yap_unify_constant(ARG2, tout);
1309}
1310
1311static Int line_count(USES_REGS1) { /* '$current_line_number'(+Stream,-N) */
1312 Term tout;
1313 int sno =
1314 Yap_CheckStream(Deref(ARG1), Input_Stream_f | Output_Stream_f | Append_Stream_f,
1315 "current_line_number/2");
1316 if (sno < 0)
1317 return (false);
1318 tout = lineCount(sno);
1319 return (Yap_unify_constant(ARG2, tout));
1320}
1321
1322static Int line_position(USES_REGS1) { /* '$line_position'(+Stream,-N) */
1323 Term tout;
1324 int sno =
1325 Yap_CheckStream(Deref(ARG1), Input_Stream_f | Output_Stream_f | Append_Stream_f,
1326 "line_position/2");
1327 if (sno < 0)
1328 return (FALSE);
1329 if (GLOBAL_Stream[sno].status & Tty_Stream_f) {
1330 Int no = 0;
1331 int i;
1332 Atom my_stream = GLOBAL_Stream[sno].name;
1333 tout = MkIntTerm(0);
1334 } else
1335 tout = MkIntTerm((1+GLOBAL_Stream[sno].charcount )-
1336 GLOBAL_Stream[sno].linestart) ;
1337 UNLOCK(GLOBAL_Stream[sno].streamlock);
1338 return (Yap_unify_constant(ARG2, tout));
1339}
1340
1341static Int character_count(USES_REGS1) { /* '$character_count'(+Stream,-N) */
1342 Term tout;
1343 int sno =
1344 Yap_CheckStream(ARG1, Input_Stream_f | Output_Stream_f | Append_Stream_f,
1345 "character_count/2");
1346 if (sno < 0)
1347 return (FALSE);
1348 if (GLOBAL_Stream[sno].status & Tty_Stream_f) {
1349 Int no = 0;
1350 int i;
1351 Atom my_stream = GLOBAL_Stream[sno].name;
1352 for (i = 0; i < MaxStreams; i++) {
1353 if (!(GLOBAL_Stream[i].status & Free_Stream_f) &&
1354 GLOBAL_Stream[i].name == my_stream)
1355 no += GLOBAL_Stream[i].charcount;
1356 }
1357 tout = MkIntTerm(no);
1358 } else if (GLOBAL_Stream[sno].status & Null_Stream_f)
1359 tout = MkIntTerm(GLOBAL_Stream[sno].charcount);
1360 else
1361 tout = MkIntTerm(ftell(GLOBAL_Stream[sno].file));
1362 UNLOCK(GLOBAL_Stream[sno].streamlock);
1363 return (Yap_unify_constant(ARG2, tout));
1364}
1365
1366static Int
1367 p_show_stream_flags(USES_REGS1) { /* '$show_stream_flags'(+Stream,Pos) */
1368 Term tout;
1369 int sno =
1370 Yap_CheckStream(ARG1, Input_Stream_f | Output_Stream_f | Append_Stream_f,
1371 "stream_property/2");
1372 if (sno < 0)
1373 return (FALSE);
1374 tout = MkIntTerm(GLOBAL_Stream[sno].status);
1375 UNLOCK(GLOBAL_Stream[sno].streamlock);
1376 return (Yap_unify(ARG2, tout));
1377}
1378
1379Term Yap_StreamPosition(int sno) { return StreamPosition(sno); }
1380
1381static Int p_show_stream_position(
1382 USES_REGS1) { /* '$show_stream_position'(+Stream,Pos) */
1383 Term tout;
1384 int sno =
1385 Yap_CheckStream(ARG1, Input_Stream_f | Output_Stream_f | Append_Stream_f,
1386 "stream_position/2");
1387 if (sno < 0)
1388 return (FALSE);
1389 tout = StreamPosition(sno);
1390 UNLOCK(GLOBAL_Stream[sno].streamlock);
1391 return Yap_unify(ARG2, tout);
1392}
1393
1394static Int
1395 set_stream_position(USES_REGS1) { /* '$set_stream_position'(+Stream,Pos) */
1396 Term tin, tp;
1397 Int char_pos;
1398 int sno =
1399 Yap_CheckStream(ARG1, Input_Stream_f | Output_Stream_f | Append_Stream_f,
1400 "set_stream_position/2");
1401 if (sno < 0) {
1402 return false;
1403 }
1404 tin = Deref(ARG2);
1405 if (IsVarTerm(tin)) {
1406 UNLOCK(GLOBAL_Stream[sno].streamlock);
1407 Yap_Error(INSTANTIATION_ERROR, tin, "set_stream_position/2");
1408 return (FALSE);
1409 } else if (!(IsApplTerm(tin))) {
1410 UNLOCK(GLOBAL_Stream[sno].streamlock);
1411 Yap_Error(DOMAIN_ERROR_STREAM_POSITION, tin, "set_stream_position/2");
1412 return (FALSE);
1413 }
1414 if (FunctorOfTerm(tin) == FunctorStreamPos) {
1415 if (IsVarTerm(tp = ArgOfTerm(1, tin))) {
1416 UNLOCK(GLOBAL_Stream[sno].streamlock);
1417 Yap_Error(INSTANTIATION_ERROR, tp, "set_stream_position/2");
1418 return (FALSE);
1419 } else if (!IsIntTerm(tp)) {
1420 UNLOCK(GLOBAL_Stream[sno].streamlock);
1421 Yap_Error(DOMAIN_ERROR_STREAM_POSITION, tin, "set_stream_position/2");
1422 return (FALSE);
1423 }
1424 if (!(GLOBAL_Stream[sno].status & Seekable_Stream_f)) {
1425 UNLOCK(GLOBAL_Stream[sno].streamlock);
1426 Yap_Error(PERMISSION_ERROR_REPOSITION_STREAM, ARG1,
1427 "set_stream_position/2");
1428 return (FALSE);
1429 }
1430 char_pos = IntOfTerm(tp);
1431 if (IsVarTerm(tp = ArgOfTerm(2, tin))) {
1432 UNLOCK(GLOBAL_Stream[sno].streamlock);
1433 Yap_Error(INSTANTIATION_ERROR, tp, "set_stream_position/2");
1434 return (FALSE);
1435 } else if (!IsIntTerm(tp)) {
1436 UNLOCK(GLOBAL_Stream[sno].streamlock);
1437 Yap_Error(DOMAIN_ERROR_STREAM_POSITION, tin, "set_stream_position/2");
1438 return (FALSE);
1439 }
1440 GLOBAL_Stream[sno].charcount = char_pos;
1441 if (IsVarTerm(tp = ArgOfTerm(3, tin))) {
1442 UNLOCK(GLOBAL_Stream[sno].streamlock);
1443 Yap_Error(INSTANTIATION_ERROR, tp, "set_stream_position/2");
1444 return (FALSE);
1445 } else if (!IsIntTerm(tp)) {
1446 UNLOCK(GLOBAL_Stream[sno].streamlock);
1447 Yap_Error(DOMAIN_ERROR_STREAM_POSITION, tin, "set_stream_position/2");
1448 return (FALSE);
1449 }
1450 int linecount = IntOfTerm(tp);
1451 GLOBAL_Stream[sno].linestart = char_pos-linecount;
1452 if (fseek(GLOBAL_Stream[sno].file, (long)(char_pos), 0) == -1) {
1453 UNLOCK(GLOBAL_Stream[sno].streamlock);
1454 Yap_Error(SYSTEM_ERROR_INTERNAL, tp,
1455 "fseek failed for set_stream_position/2");
1456 return (FALSE);
1457 }
1458 GLOBAL_Stream[sno].stream_getc = PlGetc;
1459 } else if (FunctorOfTerm(tin) == FunctorStreamEOS) {
1460 if (IsVarTerm(tp = ArgOfTerm(1, tin))) {
1461 UNLOCK(GLOBAL_Stream[sno].streamlock);
1462 Yap_Error(INSTANTIATION_ERROR, tp, "set_stream_position/2");
1463 return (FALSE);
1464 } else if (tp != MkAtomTerm(AtomAt)) {
1465 UNLOCK(GLOBAL_Stream[sno].streamlock);
1466 Yap_Error(DOMAIN_ERROR_STREAM_POSITION, tin, "set_stream_position/2");
1467 return (FALSE);
1468 }
1469 if (!(GLOBAL_Stream[sno].status & Seekable_Stream_f)) {
1470 UNLOCK(GLOBAL_Stream[sno].streamlock);
1471 PlIOError(PERMISSION_ERROR_REPOSITION_STREAM, ARG1,
1472 "set_stream_position/2");
1473 return (FALSE);
1474 }
1475 if (GLOBAL_Stream[sno].vfs) {
1476 if (GLOBAL_Stream[sno].vfs->seek &&
1477 GLOBAL_Stream[sno].vfs->seek(sno, 0L, SEEK_END) == -1) {
1478 UNLOCK(GLOBAL_Stream[sno].streamlock);
1479 PlIOError(SYSTEM_ERROR_INTERNAL, tp,
1480 "fseek failed for set_stream_position/2: %s",
1481 strerror(errno));
1482 return (FALSE);
1483 }
1484 } else if (fseek(GLOBAL_Stream[sno].file, 0L, SEEK_END) == -1) {
1485 UNLOCK(GLOBAL_Stream[sno].streamlock);
1486 PlIOError(SYSTEM_ERROR_INTERNAL, tp,
1487 "fseek failed for set_stream_position/2: %s", strerror(errno));
1488 return (FALSE);
1489 }
1490 GLOBAL_Stream[sno].stream_getc = PlGetc;
1491 /* reset the counters */
1492 GLOBAL_Stream[sno].linestart = 0;
1493 GLOBAL_Stream[sno].linecount = 1;
1494 GLOBAL_Stream[sno].charcount = 0;
1495 }
1496 UNLOCK(GLOBAL_Stream[sno].streamlock);
1497 return (TRUE);
1498}
1499
1500#if HAVE_SELECT
1501/* stream_select(+Streams,+TimeOut,-Result) */
1502static Int p_stream_select(USES_REGS1) {
1503 Term t1 = Deref(ARG1), t2;
1504 fd_set readfds, writefds, exceptfds;
1505 struct timeval timeout, *ptime;
1506
1507#if _MSC_VER
1508 u_int fdmax = 0;
1509#else
1510 int fdmax = 0;
1511#endif
1512 Term tout = TermNil, ti, Head;
1513
1514 if (IsVarTerm(t1)) {
1515 Yap_Error(INSTANTIATION_ERROR, t1, "stream_select/3");
1516 return FALSE;
1517 }
1518 if (!IsPairTerm(t1)) {
1519 Yap_Error(TYPE_ERROR_LIST, t1, "stream_select/3");
1520 return (FALSE);
1521 }
1522 FD_ZERO(&readfds);
1523 FD_ZERO(&writefds);
1524 FD_ZERO(&exceptfds);
1525 ti = t1;
1526 while (ti != TermNil) {
1527#if _MSC_VER
1528 u_int fd;
1529#else
1530 int fd;
1531#endif
1532 int sno;
1533
1534 Head = HeadOfTerm(ti);
1535 sno = Yap_CheckStream(Head, Input_Stream_f, "stream_select/3");
1536 if (sno < 0)
1537 return (FALSE);
1538 fd = GetStreamFd(sno);
1539 FD_SET(fd, &readfds);
1540 UNLOCK(GLOBAL_Stream[sno].streamlock);
1541 if (fd > fdmax)
1542 fdmax = fd;
1543 ti = TailOfTerm(ti);
1544 }
1545 t2 = Deref(ARG2);
1546 if (IsVarTerm(t2)) {
1547 Yap_Error(INSTANTIATION_ERROR, t2, "stream_select/3");
1548 return (FALSE);
1549 }
1550 if (IsAtomTerm(t2)) {
1551 if (t2 == MkAtomTerm(AtomOff)) {
1552 /* wait indefinitely */
1553 ptime = NULL;
1554 } else {
1555 Yap_Error(DOMAIN_ERROR_TIMEOUT_SPEC, t1, "stream_select/3");
1556 return (FALSE);
1557 }
1558 } else {
1559 Term t21, t22;
1560
1561 if (!IsApplTerm(t2) || FunctorOfTerm(t2) != FunctorModule) {
1562 Yap_Error(DOMAIN_ERROR_TIMEOUT_SPEC, t2, "stream_select/3");
1563 return (FALSE);
1564 }
1565 t21 = ArgOfTerm(1, t2);
1566 if (IsVarTerm(t21)) {
1567 Yap_Error(INSTANTIATION_ERROR, t2, "stream_select/3");
1568 return (FALSE);
1569 }
1570 if (!IsIntegerTerm(t21)) {
1571 Yap_Error(DOMAIN_ERROR_TIMEOUT_SPEC, t2, "stream_select/3");
1572 return (FALSE);
1573 }
1574 timeout.tv_sec = IntegerOfTerm(t21);
1575 if (timeout.tv_sec < 0) {
1576 Yap_Error(DOMAIN_ERROR_TIMEOUT_SPEC, t2, "stream_select/3");
1577 return (FALSE);
1578 }
1579 t22 = ArgOfTerm(2, t2);
1580 if (IsVarTerm(t22)) {
1581 Yap_Error(INSTANTIATION_ERROR, t2, "stream_select/3");
1582 return (FALSE);
1583 }
1584 if (!IsIntegerTerm(t22)) {
1585 Yap_Error(DOMAIN_ERROR_TIMEOUT_SPEC, t2, "stream_select/3");
1586 return (FALSE);
1587 }
1588 timeout.tv_usec = IntegerOfTerm(t22);
1589 if (timeout.tv_usec < 0) {
1590 Yap_Error(DOMAIN_ERROR_TIMEOUT_SPEC, t2, "stream_select/3");
1591 return (FALSE);
1592 }
1593 ptime = &timeout;
1594 }
1595 /* do the real work */
1596 if (select(fdmax + 1, &readfds, &writefds, &exceptfds, ptime) < 0) {
1597#if HAVE_STRERROR
1598 Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil, "stream_select/3 (select: %s)",
1599 strerror(errno));
1600#else
1601 Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil, "stream_select/3 (select)");
1602#endif
1603 }
1604 while (t1 != TermNil) {
1605 int fd;
1606 int sno;
1607
1608 Head = HeadOfTerm(t1);
1609 sno = Yap_CheckStream(Head, Input_Stream_f, "stream_select/3");
1610 fd = GetStreamFd(sno);
1611 if (FD_ISSET(fd, &readfds))
1612 tout = MkPairTerm(Head, tout);
1613 else
1614 tout = MkPairTerm(TermNil, tout);
1615 UNLOCK(GLOBAL_Stream[sno].streamlock);
1616 t1 = TailOfTerm(t1);
1617 }
1618 /* we're done, just pass the info back */
1619 return (Yap_unify(ARG3, tout));
1620}
1621#endif
1622
1623Int Yap_StreamToFileNo(Term t) {
1624 int sno =
1625 Yap_CheckStream(t, (Input_Stream_f | Output_Stream_f), "StreamToFileNo");
1626 if (GLOBAL_Stream[sno].status & Pipe_Stream_f) {
1627 UNLOCK(GLOBAL_Stream[sno].streamlock);
1628 return (GLOBAL_Stream[sno].u.pipe.fd);
1629#if HAVE_SOCKET
1630 } else if (GLOBAL_Stream[sno].status & Socket_Stream_f) {
1631 UNLOCK(GLOBAL_Stream[sno].streamlock);
1632 return (GLOBAL_Stream[sno].u.socket.fd);
1633#endif
1634 } else if (GLOBAL_Stream[sno].status & (Null_Stream_f)) {
1635 UNLOCK(GLOBAL_Stream[sno].streamlock);
1636 return (-1);
1637 } else {
1638 UNLOCK(GLOBAL_Stream[sno].streamlock);
1639 return (fileno(GLOBAL_Stream[sno].file));
1640 }
1641}
1642
1643static Int p_stream(USES_REGS1) {
1644 Term in = Deref(ARG1);
1645 if (IsVarTerm(in))
1646 return (FALSE);
1647 if (IsAtomTerm(in))
1648 return (Yap_CheckAlias(AtomOfTerm(in)) >= 0);
1649 if (IsApplTerm(in))
1650 return (FunctorOfTerm(in) == FunctorStream);
1651 return (FALSE);
1652}
1653
1654FILE *Yap_FileDescriptorFromStream(Term t) {
1655 int sno = Yap_CheckStream(t, Input_Stream_f | Output_Stream_f,
1656 "FileDescriptorFromStream");
1657 FILE *rc;
1658 if (sno < 0)
1659 return NULL;
1660 if (GLOBAL_Stream[sno].status &
1661 (Null_Stream_f | Socket_Stream_f | Pipe_Stream_f | Free_Stream_f))
1662 rc = NULL;
1663 else
1664 rc = GLOBAL_Stream[sno].file;
1665 UNLOCK(GLOBAL_Stream[sno].streamlock);
1666 return rc;
1667}
1668
1669void Yap_InitBackIO(void) {
1670 Yap_InitCPredBack("stream_property", 2, 2, stream_property,
1671 cont_stream_property, SafePredFlag | SyncPredFlag);
1672}
1673
1674void Yap_InitIOStreams(void) {
1675 Yap_InitCPred("$stream_flags", 2, stream_flags,
1676 SafePredFlag | SyncPredFlag | HiddenPredFlag);
1677 Yap_InitCPred("$check_stream", 2, p_check_stream,
1678 SafePredFlag | SyncPredFlag | HiddenPredFlag);
1679 Yap_InitCPred("$check_stream", 1, p_check_if_stream,
1680 SafePredFlag | SyncPredFlag | HiddenPredFlag | HiddenPredFlag);
1681 Yap_InitCPred("line_position", 2, line_position,
1682 SafePredFlag | SyncPredFlag | HiddenPredFlag);
1683 Yap_InitCPred("character_count", 2, character_count,
1684 SafePredFlag | SyncPredFlag | HiddenPredFlag);
1685 Yap_InitCPred("$show_stream_flags", 2, p_show_stream_flags,
1686 SafePredFlag | SyncPredFlag | HiddenPredFlag);
1687 Yap_InitCPred("$user_file_name", 2, p_user_file_name,
1688 SafePredFlag | SyncPredFlag),
1689 Yap_InitCPred("$file_name", 2, p_file_name, SafePredFlag | SyncPredFlag),
1690 Yap_InitCPred("current_input", 1, current_input,
1691 SafePredFlag | SyncPredFlag);
1692 Yap_InitCPred("current_output", 1, current_output,
1693 SafePredFlag | SyncPredFlag);
1694 Yap_InitCPred("current_error", 1, current_error,
1695 SafePredFlag | SyncPredFlag);
1696 Yap_InitCPred("set_input", 1, set_input, SafePredFlag | SyncPredFlag);
1697 Yap_InitCPred("set_output", 1, set_output, SafePredFlag | SyncPredFlag);
1698 Yap_InitCPred("set_error", 1, set_error, SafePredFlag | SyncPredFlag);
1699 Yap_InitCPred("$stream", 1, p_stream, SafePredFlag | TestPredFlag);
1700 Yap_InitCPred("$clear_input", 1, clear_input, SafePredFlag | TestPredFlag);
1701
1702#if HAVE_SELECT
1703 Yap_InitCPred("stream_select", 3, p_stream_select,
1704 SafePredFlag | SyncPredFlag);
1705#endif
1706 Yap_InitCPred("line_count", 2, line_count, SafePredFlag | SyncPredFlag);
1707 Yap_InitCPred("$show_stream_position", 2, p_show_stream_position,
1708 SafePredFlag | SyncPredFlag | HiddenPredFlag);
1709 Yap_InitCPred("set_stream_position", 2, set_stream_position,
1710 SafePredFlag | SyncPredFlag);
1711 Yap_InitCPred("set_stream", 2, set_stream, SafePredFlag | SyncPredFlag);
1712}
Main definitions.
void Yap_SetAlias(Atom arg, int sno)
set arg as an alias to sno, but first check if not done before
Definition: alias.c:259
bool Yap_DeleteAliases(int sno)
purge all aliases for stream sno
Definition: alias.c:267
void * Malloc(size_t sz USES_REGS)
allocate a temporary text block
Definition: alloc.c:1759
@ encoding
support for coding systens, YAP relies on UTF-8 internally
Definition: YapLFlagInfo.h:83
Definition: VFS.h:74
int64_t(* seek)(int sno, int64_t offset, int whence)
flush a stream
Definition: VFS.h:94
bool(* close)(int sno)
open an object
Definition: VFS.h:86
Definition: YapFlags.h:152