1 /****************************************************************************
2 **
3 ** This file is part of GAP, a system for computational discrete algebra.
4 **
5 ** Copyright of GAP belongs to its developers, whose names are too numerous
6 ** to list here. Please refer to the COPYRIGHT file for details.
7 **
8 ** SPDX-License-Identifier: GPL-2.0-or-later
9 **
10 ** This file contains the various read-eval-print loops and streams related
11 ** stuff. The system depend part is in "sysfiles.c".
12 */
13
14 #include "streams.h"
15
16 #include "bool.h"
17 #include "calls.h"
18 #include "error.h"
19 #include "funcs.h"
20 #include "gap.h"
21 #include "gapstate.h"
22 #include "gvars.h"
23 #include "integer.h"
24 #include "io.h"
25 #include "lists.h"
26 #include "modules.h"
27 #include "opers.h"
28 #include "plist.h"
29 #include "precord.h"
30 #include "read.h"
31 #include "records.h"
32 #include "stats.h"
33 #include "stringobj.h"
34 #include "sysfiles.h"
35 #include "sysopt.h"
36 #include "vars.h"
37
38 #include <dirent.h>
39 #include <errno.h>
40 #include <time.h>
41 #include <unistd.h>
42
43 #ifdef HAVE_SELECT
44 // For FuncUNIXSelect
45 #include <sys/time.h>
46 #endif
47
48
49 static Obj IsInputStream;
50 static Obj IsOutputStream;
51
52 #define RequireInputStream(funcname, op) \
53 RequireArgumentCondition(funcname, op, \
54 CALL_1ARGS(IsInputStream, op) == True, \
55 "must be an input stream")
56
57 #define RequireOutputStream(funcname, op) \
58 RequireArgumentCondition(funcname, op, \
59 CALL_1ARGS(IsOutputStream, op) == True, \
60 "must be an output stream")
61
62
63 /****************************************************************************
64 **
65 *F * * * * * * * * * streams and files related functions * * * * * * * * * *
66 */
67
READ_COMMAND(Obj * evalResult)68 static Int READ_COMMAND(Obj *evalResult)
69 {
70 ExecStatus status;
71
72 ClearError();
73 status = ReadEvalCommand(STATE(BottomLVars), evalResult, 0);
74 if( status == STATUS_EOF )
75 return 0;
76
77 if ( STATE(UserHasQuit) || STATE(UserHasQUIT) )
78 return 0;
79
80 /* handle return-value or return-void command */
81 if ( status & (STATUS_RETURN_VAL | STATUS_RETURN_VOID) ) {
82 Pr( "'return' must not be used in file read-eval loop\n", 0L, 0L );
83 }
84
85 /* handle quit command */
86 else if (status == STATUS_QUIT) {
87 SetRecursionDepth(0);
88 STATE(UserHasQuit) = 1;
89 }
90 else if (status == STATUS_QQUIT) {
91 STATE(UserHasQUIT) = 1;
92 }
93 ClearError();
94
95 return 1;
96 }
97
98 /****************************************************************************
99 **
100 *F FuncREAD_ALL_COMMANDS( <self>, <instream>, <echo>, <capture>, <outputFunc> )
101 **
102 ** FuncREAD_ALL_COMMANDS attempts to execute all statements read from the
103 ** stream <instream>. It returns 'fail' if the stream cannot be opened,
104 ** otherwise a list of lists, each entry of which reflects the result of the
105 ** execution of one statement.
106 **
107 ** If the parameter <echo> is 'true', then the statements are echoed to the
108 ** current output.
109 **
110 ** If the parameter <capture> is 'true', then any output occurring during
111 ** execution of a statement, including the output of <outputFunc>, is
112 ** captured into a string.
113 **
114 ** If <resultCallback> is a function, then this function is called on every
115 ** statement result, otherwise this parameter is ignored. Possible outputs of
116 ** this function are captured if <capture> is 'true'.
117 **
118 ** The results are returned as lists of length at most five, the structure of
119 ** which is explained below:
120 **
121 ** - The first entry is 'true' if the statement was executed successfully,
122 ** and 'false' otherwise.
123 **
124 ** - If the first entry is 'true', then the second entry is bound to the
125 ** result of the statement if there was one, and unbound otherwise.
126 **
127 ** - The third entry is 'true' if the statement ended in a dual semicolon,
128 ** and 'false' otherwise.
129 **
130 ** - The fourth entry contains the return value of <resultCallback> if
131 ** applicable.
132 **
133 ** - The fifth entry contains the captured output as a string, if <capture>
134 ** is 'true'.
135 **
136 ** This function is currently used in interactive tools such as the GAP
137 ** Jupyter kernel to execute cells and is likely to be replaced by a function
138 ** that can read a single command from a stream without losing the rest of
139 ** its content.
140 */
READ_ALL_COMMANDS(Obj instream,Obj echo,Obj capture,Obj resultCallback)141 Obj READ_ALL_COMMANDS(Obj instream, Obj echo, Obj capture, Obj resultCallback)
142 {
143 ExecStatus status;
144 UInt dualSemicolon;
145 Obj result, resultList;
146 Obj copy;
147 Obj evalResult;
148 Obj outstream = 0;
149 Obj outstreamString = 0;
150
151 RequireInputStream("READ_ALL_COMMANDS", instream);
152
153 /* try to open the streams */
154 if (!OpenInputStream(instream, echo == True)) {
155 return Fail;
156 }
157
158
159 if (capture == True) {
160 outstreamString = NEW_STRING(0);
161 outstream = DoOperation2Args(ValGVar(GVarName("OutputTextString")),
162 outstreamString, True);
163 }
164 if (outstream && !OpenOutputStream(outstream)) {
165 CloseInput();
166 return Fail;
167 }
168
169 resultList = NEW_PLIST(T_PLIST, 16);
170
171 do {
172 ClearError();
173 if (outstream) {
174 // Clean in case there has been any output
175 SET_LEN_STRING(outstreamString, 0);
176 }
177
178 status =
179 ReadEvalCommand(STATE(BottomLVars), &evalResult, &dualSemicolon);
180
181 if (!(status & (STATUS_EOF | STATUS_QUIT | STATUS_QQUIT))) {
182 result = NEW_PLIST(T_PLIST, 5);
183 AssPlist(result, 1, False);
184 PushPlist(resultList, result);
185
186 if (!(status & STATUS_ERROR)) {
187
188 AssPlist(result, 1, True);
189 AssPlist(result, 3, dualSemicolon ? True : False);
190
191 if (evalResult) {
192 AssPlist(result, 2, evalResult);
193 }
194
195 if (evalResult && IS_FUNC(resultCallback) && !dualSemicolon) {
196 Obj tmp = CALL_1ARGS(resultCallback, evalResult);
197 AssPlist(result, 4, tmp);
198 }
199 }
200 // Capture output
201 if (capture == True) {
202 // Flush output
203 Pr("\03", 0L, 0L);
204 copy = CopyToStringRep(outstreamString);
205 SET_LEN_STRING(outstreamString, 0);
206 AssPlist(result, 5, copy);
207 }
208 }
209 } while (!(status & (STATUS_EOF | STATUS_QUIT | STATUS_QQUIT)));
210
211 if (outstream)
212 CloseOutput();
213 CloseInput();
214 ClearError();
215
216 return resultList;
217 }
218
FuncREAD_ALL_COMMANDS(Obj self,Obj instream,Obj echo,Obj capture,Obj resultCallback)219 static Obj FuncREAD_ALL_COMMANDS(
220 Obj self, Obj instream, Obj echo, Obj capture, Obj resultCallback)
221 {
222 return READ_ALL_COMMANDS(instream, echo, capture, resultCallback);
223 }
224
225
226 /*
227 Returns a list with one or two entries. The first
228 entry is set to "false" if there was any error
229 executing the command, and "true" otherwise.
230 The second entry, if present, is the return value of
231 the command. If it not present, the command returned nothing.
232 */
FuncREAD_COMMAND_REAL(Obj self,Obj stream,Obj echo)233 static Obj FuncREAD_COMMAND_REAL(Obj self, Obj stream, Obj echo)
234 {
235 Int status;
236 Obj result;
237 Obj evalResult;
238
239 RequireInputStream("READ_COMMAND_REAL", stream);
240
241 result = NEW_PLIST( T_PLIST, 2 );
242 SET_LEN_PLIST(result, 1);
243 SET_ELM_PLIST(result, 1, False);
244
245 /* try to open the file */
246 if (!OpenInputStream(stream, echo == True)) {
247 return result;
248 }
249
250 status = READ_COMMAND(&evalResult);
251
252 CloseInput();
253
254 if( status == 0 ) return result;
255
256 if (STATE(UserHasQUIT)) {
257 STATE(UserHasQUIT) = 0;
258 return result;
259 }
260
261 if (STATE(UserHasQuit)) {
262 STATE(UserHasQuit) = 0;
263 }
264
265 SET_ELM_PLIST(result, 1, True);
266 if (evalResult) {
267 SET_LEN_PLIST(result, 2);
268 SET_ELM_PLIST(result, 2, evalResult);
269 }
270 return result;
271 }
272
273 /****************************************************************************
274 **
275 *F READ() . . . . . . . . . . . . . . . . . . . . . . . read current input
276 **
277 ** Read the current input and close the input stream.
278 */
279
280 static UInt LastReadValueGVar;
281
READ_INNER(UInt UseUHQ)282 static Int READ_INNER ( UInt UseUHQ )
283 {
284 if (STATE(UserHasQuit))
285 {
286 Pr("Warning: Entering READ with UserHasQuit set, this should never happen, resetting",0,0);
287 STATE(UserHasQuit) = 0;
288 }
289 if (STATE(UserHasQUIT))
290 {
291 Pr("Warning: Entering READ with UserHasQUIT set, this should never happen, resetting",0,0);
292 STATE(UserHasQUIT) = 0;
293 }
294 MakeReadWriteGVar(LastReadValueGVar);
295 AssGVar( LastReadValueGVar, 0);
296 MakeReadOnlyGVar(LastReadValueGVar);
297 /* now do the reading */
298 while ( 1 ) {
299 ClearError();
300 Obj evalResult;
301 ExecStatus status = ReadEvalCommand(STATE(BottomLVars), &evalResult, 0);
302 if (STATE(UserHasQuit) || STATE(UserHasQUIT))
303 break;
304
305 /* handle return-value or return-void command */
306 if ( status & (STATUS_RETURN_VAL | STATUS_RETURN_VOID) ) {
307 Pr(
308 "'return' must not be used in file read-eval loop\n",
309 0L, 0L );
310 }
311
312 /* handle quit command or <end-of-file> */
313 else if ( status & (STATUS_ERROR | STATUS_EOF))
314 break;
315 else if (status == STATUS_QUIT) {
316 SetRecursionDepth(0);
317 STATE(UserHasQuit) = 1;
318 break;
319 }
320 else if (status == STATUS_QQUIT) {
321 STATE(UserHasQUIT) = 1;
322 break;
323 }
324 if (evalResult)
325 {
326 MakeReadWriteGVar(LastReadValueGVar);
327 AssGVar( LastReadValueGVar, evalResult);
328 MakeReadOnlyGVar(LastReadValueGVar);
329 }
330
331 }
332
333
334 /* close the input file again, and return 'true' */
335 if ( ! CloseInput() ) {
336 ErrorQuit(
337 "Panic: READ cannot close input, this should not happen",
338 0L, 0L );
339 }
340 ClearError();
341
342 if (!UseUHQ && STATE(UserHasQuit)) {
343 STATE(UserHasQuit) = 0; /* stop recovery here */
344 return 2;
345 }
346
347 return 1;
348 }
349
350
READ(void)351 static Int READ( void ) {
352 return READ_INNER(1);
353 }
354
READ_NORECOVERY(void)355 static Int READ_NORECOVERY( void ) {
356 return READ_INNER(0);
357 }
358
359 /****************************************************************************
360 **
361 *F READ_AS_FUNC() . . . . . . . . . . . . . read current input as function
362 **
363 ** Read the current input as function and close the input stream.
364 */
READ_AS_FUNC(void)365 Obj READ_AS_FUNC ( void )
366 {
367 /* now do the reading */
368 ClearError();
369 Obj evalResult;
370 UInt type = ReadEvalFile(&evalResult);
371
372 /* get the function */
373 Obj func = (type == 0) ? evalResult : Fail;
374
375 /* close the input file again, and return 'true' */
376 if ( ! CloseInput() ) {
377 ErrorQuit(
378 "Panic: READ_AS_FUNC cannot close input, this should not happen",
379 0L, 0L );
380 }
381 ClearError();
382
383 /* return the function */
384 return func;
385 }
386
387
READ_TEST_OR_LOOP(Obj context)388 static void READ_TEST_OR_LOOP(Obj context)
389 {
390 UInt type;
391 UInt oldtime;
392 UInt dualSemicolon;
393
394 /* get the starting time */
395 oldtime = SyTime();
396
397 /* now do the reading */
398 while ( 1 ) {
399
400 /* read and evaluate the command */
401 ClearError();
402 Obj evalResult;
403 type = ReadEvalCommand(context, &evalResult, &dualSemicolon);
404
405 /* stop the stopwatch */
406 AssGVarWithoutReadOnlyCheck(Time, ObjInt_Int(SyTime() - oldtime));
407
408 /* handle ordinary command */
409 if ( type == 0 && evalResult != 0 ) {
410
411 /* remember the value in 'last' and the time in 'time' */
412 UpdateLast(evalResult, 3);
413
414 /* print the result */
415 if ( ! dualSemicolon ) {
416 Bag currLVars = STATE(CurrLVars); /* in case view runs into error */
417 ViewObjHandler( evalResult );
418 SWITCH_TO_OLD_LVARS(currLVars);
419 }
420 }
421
422 /* handle return-value or return-void command */
423 else if ( type & (STATUS_RETURN_VAL | STATUS_RETURN_VOID) ) {
424 Pr( "'return' must not be used in file read-eval loop\n",
425 0L, 0L );
426 }
427
428 /* handle quit command or <end-of-file> */
429 else if ( type & (STATUS_QUIT | STATUS_EOF) ) {
430 break;
431 }
432 // FIXME: what about other types? e.g. STATUS_ERROR and STATUS_QQUIT
433
434 }
435
436 ClearError();
437 }
438
439
440 /****************************************************************************
441 **
442 *F READ_GAP_ROOT( <filename> ) . . . read from gap root, dyn-load or static
443 **
444 ** 'READ_GAP_ROOT' tries to find a file under the root directory, it will
445 ** search all directories given in 'SyGapRootPaths', check dynamically
446 ** loadable modules and statically linked modules.
447 */
READ_GAP_ROOT(const Char * filename)448 Int READ_GAP_ROOT ( const Char * filename )
449 {
450 TypGRF_Data result;
451 Int res;
452 UInt type;
453
454 /* try to find the file */
455 res = SyFindOrLinkGapRootFile( filename, &result );
456
457 /* not found */
458 if ( res == 0 ) {
459 return 0;
460 }
461
462 // statically linked
463 else if (res == 2) {
464 // This code section covers transparently loading GAC compiled
465 // versions of GAP source files, by running code similar to that in
466 // FuncLOAD_STAT. For example, lib/oper1.g is compiled into C code
467 // which is stored in src/c_oper1.c; when reading lib/oper1.g, we
468 // instead will load its compiled version.
469 if ( SyDebugLoading ) {
470 Pr("#I READ_GAP_ROOT: loading '%s' statically\n", (Int)filename,
471 0);
472 }
473 ActivateModule(result.module_info);
474 RecordLoadedModule(result.module_info, 1, filename);
475 return 1;
476 }
477
478 /* special handling for the other cases, if we are trying to load compiled
479 modules needed for a saved workspace ErrorQuit is not available */
480 else if (SyRestoring) {
481 if ( res == 3 ) {
482 Pr("Can't find compiled module '%s' needed by saved workspace\n",
483 (Int) filename, 0L);
484 return 0;
485 }
486 Pr("unknown result code %d from 'SyFindGapRoot'", res, 0L );
487 SyExit(1);
488 }
489
490 /* ordinary gap file */
491 else if ( res == 3 ) {
492 if ( SyDebugLoading ) {
493 Pr( "#I READ_GAP_ROOT: loading '%s' as GAP file\n",
494 (Int)filename, 0L );
495 }
496 if ( OpenInput(result.path) ) {
497 while ( 1 ) {
498 ClearError();
499 Obj evalResult;
500 type = ReadEvalCommand(STATE(BottomLVars), &evalResult, 0);
501 if (STATE(UserHasQuit) || STATE(UserHasQUIT))
502 break;
503 if ( type & (STATUS_RETURN_VAL | STATUS_RETURN_VOID) ) {
504 Pr( "'return' must not be used in file", 0L, 0L );
505 }
506 else if ( type & (STATUS_QUIT | STATUS_EOF) ) {
507 break;
508 }
509 }
510 CloseInput();
511 ClearError();
512 return 1;
513 }
514 }
515
516 /* don't know */
517 else {
518 ErrorQuit( "unknown result code %d from 'SyFindGapRoot'", res, 0L );
519 }
520 return 0;
521 }
522
523
524 /****************************************************************************
525 **
526 *F FuncCLOSE_LOG_TO() . . . . . . . . . . . . . . . . . . . . stop logging
527 **
528 ** 'FuncCLOSE_LOG_TO' implements a method for 'LogTo'.
529 **
530 ** 'LogTo()'
531 **
532 ** 'LogTo' called with no argument closes the current logfile again, so that
533 ** input from '*stdin*' and '*errin*' and output to '*stdout*' and
534 ** '*errout*' will no longer be echoed to a file.
535 */
FuncCLOSE_LOG_TO(Obj self)536 static Obj FuncCLOSE_LOG_TO(Obj self)
537 {
538 if ( ! CloseLog() ) {
539 ErrorQuit("LogTo: can not close the logfile",0L,0L);
540 }
541 return True;
542 }
543
544
545 /****************************************************************************
546 **
547 *F FuncLOG_TO( <filename> ) . . . . . . . . . . . . start logging to a file
548 **
549 ** 'FuncLOG_TO' implements a method for 'LogTo'
550 **
551 ** 'LogTo( <filename> )'
552 **
553 ** 'LogTo' instructs GAP to echo all input from the standard input files,
554 ** '*stdin*' and '*errin*' and all output to the standard output files,
555 ** '*stdout*' and '*errout*', to the file with the name <filename>.
556 ** The file is created if it does not exist, otherwise it is truncated.
557 */
FuncLOG_TO(Obj self,Obj filename)558 static Obj FuncLOG_TO(Obj self, Obj filename)
559 {
560 RequireStringRep("LogTo", filename);
561 if ( ! OpenLog( CONST_CSTR_STRING(filename) ) ) {
562 ErrorReturnVoid( "LogTo: cannot log to %g",
563 (Int)filename, 0L,
564 "you can 'return;'" );
565 return False;
566 }
567 return True;
568 }
569
570
571 /****************************************************************************
572 **
573 *F FuncLOG_TO_STREAM( <stream> ) . . . . . . . . . start logging to a stream
574 */
FuncLOG_TO_STREAM(Obj self,Obj stream)575 static Obj FuncLOG_TO_STREAM(Obj self, Obj stream)
576 {
577 RequireOutputStream("LogTo", stream);
578 if ( ! OpenLogStream(stream) ) {
579 ErrorReturnVoid( "LogTo: cannot log to stream", 0L, 0L,
580 "you can 'return;'" );
581 return False;
582 }
583 return True;
584 }
585
586
587 /****************************************************************************
588 **
589 *F FuncCLOSE_INPUT_LOG_TO() . . . . . . . . . . . . . . . . . stop logging
590 **
591 ** 'FuncCLOSE_INPUT_LOG_TO' implements a method for 'InputLogTo'.
592 **
593 ** 'InputLogTo()'
594 **
595 ** 'InputLogTo' called with no argument closes the current logfile again, so
596 ** that input from '*stdin*' and '*errin*' will no longer be echoed to a
597 ** file.
598 */
FuncCLOSE_INPUT_LOG_TO(Obj self)599 static Obj FuncCLOSE_INPUT_LOG_TO(Obj self)
600 {
601 if ( ! CloseInputLog() ) {
602 ErrorQuit("InputLogTo: can not close the logfile",0L,0L);
603 }
604 return True;
605 }
606
607
608 /****************************************************************************
609 **
610 *F FuncINPUT_LOG_TO( <filename> ) . . . . . . . . . start logging to a file
611 **
612 ** 'FuncINPUT_LOG_TO' implements a method for 'InputLogTo'
613 **
614 ** 'InputLogTo( <filename> )'
615 **
616 ** 'InputLogTo' instructs GAP to echo all input from the standard input
617 ** files, '*stdin*' and '*errin*' to the file with the name <filename>. The
618 ** file is created if it does not exist, otherwise it is truncated.
619 */
FuncINPUT_LOG_TO(Obj self,Obj filename)620 static Obj FuncINPUT_LOG_TO(Obj self, Obj filename)
621 {
622 RequireStringRep("InputLogTo", filename);
623 if ( ! OpenInputLog( CONST_CSTR_STRING(filename) ) ) {
624 ErrorReturnVoid( "InputLogTo: cannot log to %g",
625 (Int)filename, 0L,
626 "you can 'return;'" );
627 return False;
628 }
629 return True;
630 }
631
632
633 /****************************************************************************
634 **
635 *F FuncINPUT_LOG_TO_STREAM( <stream> ) . . . . . . start logging to a stream
636 */
FuncINPUT_LOG_TO_STREAM(Obj self,Obj stream)637 static Obj FuncINPUT_LOG_TO_STREAM(Obj self, Obj stream)
638 {
639 RequireOutputStream("InputLogTo", stream);
640 if ( ! OpenInputLogStream(stream) ) {
641 ErrorReturnVoid( "InputLogTo: cannot log to stream", 0L, 0L,
642 "you can 'return;'" );
643 return False;
644 }
645 return True;
646 }
647
648
649 /****************************************************************************
650 **
651 *F FuncCLOSE_OUTPUT_LOG_TO() . . . . . . . . . . . . . . . . . stop logging
652 **
653 ** 'FuncCLOSE_OUTPUT_LOG_TO' implements a method for 'OutputLogTo'.
654 **
655 ** 'OutputLogTo()'
656 **
657 ** 'OutputLogTo' called with no argument closes the current logfile again,
658 ** so that output from '*stdin*' and '*errin*' will no longer be echoed to a
659 ** file.
660 */
FuncCLOSE_OUTPUT_LOG_TO(Obj self)661 static Obj FuncCLOSE_OUTPUT_LOG_TO(Obj self)
662 {
663 if ( ! CloseOutputLog() ) {
664 ErrorQuit("OutputLogTo: can not close the logfile",0L,0L);
665 }
666 return True;
667 }
668
669
670 /****************************************************************************
671 **
672 *F FuncOUTPUT_LOG_TO( <filename> ) . . . . . . . . start logging to a file
673 **
674 ** 'FuncOUTPUT_LOG_TO' implements a method for 'OutputLogTo'
675 **
676 ** 'OutputLogTo( <filename> )'
677 **
678 ** 'OutputLogTo' instructs GAP to echo all output from the standard output
679 ** files, '*stdin*' and '*errin*' to the file with the name <filename>. The
680 ** file is created if it does not exist, otherwise it is truncated.
681 */
FuncOUTPUT_LOG_TO(Obj self,Obj filename)682 static Obj FuncOUTPUT_LOG_TO(Obj self, Obj filename)
683 {
684 RequireStringRep("OutputLogTo", filename);
685 if ( ! OpenOutputLog( CONST_CSTR_STRING(filename) ) ) {
686 ErrorReturnVoid( "OutputLogTo: cannot log to %g",
687 (Int)filename, 0L,
688 "you can 'return;'" );
689 return False;
690 }
691 return True;
692 }
693
694
695 /****************************************************************************
696 **
697 *F FuncOUTPUT_LOG_TO_STREAM( <stream> ) . . . . . start logging to a stream
698 */
FuncOUTPUT_LOG_TO_STREAM(Obj self,Obj stream)699 static Obj FuncOUTPUT_LOG_TO_STREAM(Obj self, Obj stream)
700 {
701 RequireOutputStream("OutputLogTo", stream);
702 if ( ! OpenOutputLogStream(stream) ) {
703 ErrorReturnVoid( "OutputLogTo: cannot log to stream", 0L, 0L,
704 "you can 'return;'" );
705 return False;
706 }
707 return True;
708 }
709
710
711 /****************************************************************************
712 **
713 *F FuncPrint( <self>, <args> ) . . . . . . . . . . . . . . . . print <args>
714 */
FuncPrint(Obj self,Obj args)715 static Obj FuncPrint(Obj self, Obj args)
716 {
717 volatile Obj arg;
718 volatile UInt i;
719 syJmp_buf readJmpError;
720
721 /* print all the arguments, take care of strings and functions */
722 for ( i = 1; i <= LEN_PLIST(args); i++ ) {
723 arg = ELM_LIST(args,i);
724 if ( IS_PLIST(arg) && 0 < LEN_PLIST(arg) && IsStringConv(arg) ) {
725 PrintString1(arg);
726 }
727 else if ( IS_STRING_REP(arg) ) {
728 PrintString1(arg);
729 }
730 else if ( TNUM_OBJ( arg ) == T_FUNCTION ) {
731 PrintFunction( arg );
732 }
733 else {
734 memcpy( readJmpError, STATE(ReadJmpError), sizeof(syJmp_buf) );
735
736 /* if an error occurs stop printing */
737 TRY_IF_NO_ERROR {
738 PrintObj( arg );
739 }
740 CATCH_ERROR {
741 memcpy( STATE(ReadJmpError), readJmpError, sizeof(syJmp_buf) );
742 ReadEvalError();
743 }
744 memcpy( STATE(ReadJmpError), readJmpError, sizeof(syJmp_buf) );
745 }
746 }
747
748 return 0;
749 }
750
PRINT_OR_APPEND_TO_FILE_OR_STREAM(Obj args,int append,int file)751 static Obj PRINT_OR_APPEND_TO_FILE_OR_STREAM(Obj args, int append, int file)
752 {
753 const char * volatile funcname = append ? "AppendTo" : "PrintTo";
754 volatile Obj arg;
755 volatile Obj destination;
756 volatile UInt i;
757 syJmp_buf readJmpError;
758
759 /* first entry is the file or stream */
760 destination = ELM_LIST(args, 1);
761
762 /* try to open the output and handle failures */
763 if (file) {
764 RequireStringRep(funcname, destination);
765 i = append ? OpenAppend(CONST_CSTR_STRING(destination))
766 : OpenOutput(CONST_CSTR_STRING(destination));
767 if (!i) {
768 if (strcmp(CSTR_STRING(destination), "*errout*") == 0) {
769 Panic("Failed to open *errout*!");
770 }
771 ErrorQuit("%s: cannot open '%g' for output", (Int)funcname,
772 (Int)destination);
773 }
774 }
775 else {
776 if (CALL_1ARGS(IsOutputStream, destination) != True) {
777 ErrorQuit("%s: <outstream> must be an output stream",
778 (Int)funcname, 0L);
779 }
780 i = OpenOutputStream(destination);
781 if (!i) {
782 ErrorQuit("%s: cannot open stream for output", (Int)funcname, 0L);
783 }
784 }
785
786 /* print all the arguments, take care of strings and functions */
787 for ( i = 2; i <= LEN_PLIST(args); i++ ) {
788 arg = ELM_LIST(args,i);
789
790 /* if an error occurs stop printing */
791 memcpy(readJmpError, STATE(ReadJmpError), sizeof(syJmp_buf));
792 TRY_IF_NO_ERROR
793 {
794 if (IS_PLIST(arg) && 0 < LEN_PLIST(arg) && IsStringConv(arg)) {
795 PrintString1(arg);
796 }
797 else if (IS_STRING_REP(arg)) {
798 PrintString1(arg);
799 }
800 else if (IS_FUNC(arg)) {
801 PrintFunction(arg);
802 }
803 else {
804 PrintObj(arg);
805 }
806 }
807 CATCH_ERROR
808 {
809 CloseOutput();
810 memcpy( STATE(ReadJmpError), readJmpError, sizeof(syJmp_buf) );
811 ReadEvalError();
812 }
813 memcpy(STATE(ReadJmpError), readJmpError, sizeof(syJmp_buf));
814 }
815
816 /* close the output file again, and return nothing */
817 if ( ! CloseOutput() ) {
818 ErrorQuit( "%s: cannot close output", (Int)funcname, 0L );
819 }
820
821 return 0;
822 }
PRINT_OR_APPEND_TO(Obj args,int append)823 static Obj PRINT_OR_APPEND_TO(Obj args, int append)
824 {
825 return PRINT_OR_APPEND_TO_FILE_OR_STREAM(args, append, 1);
826 }
827
828
PRINT_OR_APPEND_TO_STREAM(Obj args,int append)829 static Obj PRINT_OR_APPEND_TO_STREAM(Obj args, int append)
830 {
831 return PRINT_OR_APPEND_TO_FILE_OR_STREAM(args, append, 0);
832 }
833
834 /****************************************************************************
835 **
836 *F FuncPRINT_TO( <self>, <args> ) . . . . . . . . . . . . . . print <args>
837 */
FuncPRINT_TO(Obj self,Obj args)838 static Obj FuncPRINT_TO(Obj self, Obj args)
839 {
840 return PRINT_OR_APPEND_TO(args, 0);
841 }
842
843
844 /****************************************************************************
845 **
846 *F FuncPRINT_TO_STREAM( <self>, <args> ) . . . . . . . . . . . print <args>
847 */
FuncPRINT_TO_STREAM(Obj self,Obj args)848 static Obj FuncPRINT_TO_STREAM(Obj self, Obj args)
849 {
850 /* Note that FuncPRINT_TO_STREAM and FuncAPPEND_TO_STREAM do exactly the
851 same, they only differ in the function name they print as part
852 of their error messages. */
853 return PRINT_OR_APPEND_TO_STREAM(args, 0);
854 }
855
856
857 /****************************************************************************
858 **
859 *F FuncAPPEND_TO( <self>, <args> ) . . . . . . . . . . . . . . append <args>
860 */
FuncAPPEND_TO(Obj self,Obj args)861 static Obj FuncAPPEND_TO(Obj self, Obj args)
862 {
863 return PRINT_OR_APPEND_TO(args, 1);
864 }
865
866
867 /****************************************************************************
868 **
869 *F FuncAPPEND_TO_STREAM( <self>, <args> ) . . . . . . . . . . append <args>
870 */
FuncAPPEND_TO_STREAM(Obj self,Obj args)871 static Obj FuncAPPEND_TO_STREAM(Obj self, Obj args)
872 {
873 /* Note that FuncPRINT_TO_STREAM and FuncAPPEND_TO_STREAM do exactly the
874 same, they only differ in the function name they print as part
875 of their error messages. */
876 return PRINT_OR_APPEND_TO_STREAM(args, 1);
877 }
878
879
880 /****************************************************************************
881 **
882 *F FuncREAD( <self>, <filename> ) . . . . . . . . . . . . . . . read a file
883 **
884 ** Read the current input and close the input stream.
885 */
FuncREAD(Obj self,Obj filename)886 static Obj FuncREAD(Obj self, Obj filename)
887 {
888 /* check the argument */
889 RequireStringRep("READ", filename);
890
891 /* try to open the file */
892 if ( ! OpenInput( CONST_CSTR_STRING(filename) ) ) {
893 return False;
894 }
895
896 /* read the test file */
897 return READ() ? True : False;
898 }
899
900 /****************************************************************************
901 **
902 *F FuncREAD_NORECOVERY( <self>, <filename> ) . . . . . . . . . read a file
903 **
904 ** Read the current input and close the input stream. Disable the normal
905 ** mechanism which ensures that quitting from a break loop gets you back to
906 ** a live prompt. This is initially designed for the files read from the
907 ** command line.
908 */
FuncREAD_NORECOVERY(Obj self,Obj input)909 static Obj FuncREAD_NORECOVERY(Obj self, Obj input)
910 {
911 if ( IsStringConv( input ) ) {
912 if ( ! OpenInput( CONST_CSTR_STRING(input) ) ) {
913 return False;
914 }
915 }
916 else if (CALL_1ARGS(IsInputStream, input) == True) {
917 if (!OpenInputStream(input, 0)) {
918 return False;
919 }
920 }
921 else {
922 return Fail;
923 }
924
925 /* read the file */
926 switch (READ_NORECOVERY()) {
927 case 0: return False;
928 case 1: return True;
929 case 2: return Fail;
930 default: return Fail;
931 }
932 }
933
934
935 /****************************************************************************
936 **
937 *F FuncREAD_STREAM( <self>, <stream> ) . . . . . . . . . . . read a stream
938 */
FuncREAD_STREAM(Obj self,Obj stream)939 static Obj FuncREAD_STREAM(Obj self, Obj stream)
940 {
941 RequireInputStream("READ_STREAM", stream);
942
943 /* try to open the file */
944 if (!OpenInputStream(stream, 0)) {
945 return False;
946 }
947
948 /* read the test file */
949 return READ() ? True : False;
950 }
951
952 /****************************************************************************
953 **
954 *F FuncREAD_STREAM_LOOP( <self>, <instream>, <outstream> ) . . read a stream
955 **
956 ** Read data from <instream> in a read-eval-view loop and write all output
957 ** to <outstream>.
958 */
FuncREAD_STREAM_LOOP_WITH_CONTEXT(Obj self,Obj instream,Obj outstream,Obj context)959 static Obj FuncREAD_STREAM_LOOP_WITH_CONTEXT(Obj self,
960 Obj instream,
961 Obj outstream,
962 Obj context)
963 {
964 Int res;
965
966 RequireInputStream("READ_STREAM_LOOP", instream);
967 RequireOutputStream("READ_STREAM_LOOP", outstream);
968
969 if (!OpenInputStream(instream, 0)) {
970 return False;
971 }
972
973 if (!OpenOutputStream(outstream)) {
974 res = CloseInput();
975 GAP_ASSERT(res);
976 return False;
977 }
978
979 LockCurrentOutput(1);
980 READ_TEST_OR_LOOP(context);
981 LockCurrentOutput(0);
982
983 res = CloseInput();
984 GAP_ASSERT(res);
985
986 res &= CloseOutput();
987 GAP_ASSERT(res);
988
989 return res ? True : False;
990 }
991
FuncREAD_STREAM_LOOP(Obj self,Obj stream,Obj catcherrstdout)992 static Obj FuncREAD_STREAM_LOOP(Obj self, Obj stream, Obj catcherrstdout)
993 {
994 return FuncREAD_STREAM_LOOP_WITH_CONTEXT(self, stream, catcherrstdout,
995 STATE(BottomLVars));
996 }
997 /****************************************************************************
998 **
999 *F FuncREAD_AS_FUNC( <self>, <filename> ) . . . . . . . . . . . read a file
1000 */
FuncREAD_AS_FUNC(Obj self,Obj filename)1001 static Obj FuncREAD_AS_FUNC(Obj self, Obj filename)
1002 {
1003 // check the argument
1004 RequireStringRep("READ_AS_FUNC", filename);
1005
1006 /* try to open the file */
1007 if ( ! OpenInput( CONST_CSTR_STRING(filename) ) ) {
1008 return Fail;
1009 }
1010
1011 /* read the function */
1012 return READ_AS_FUNC();
1013 }
1014
1015
1016 /****************************************************************************
1017 **
1018 *F FuncREAD_AS_FUNC_STREAM( <self>, <stream> ) . . . . . . . . read a file
1019 */
FuncREAD_AS_FUNC_STREAM(Obj self,Obj stream)1020 static Obj FuncREAD_AS_FUNC_STREAM(Obj self, Obj stream)
1021 {
1022 RequireInputStream("READ_AS_FUNC_STREAM", stream);
1023
1024 /* try to open the file */
1025 if (!OpenInputStream(stream, 0)) {
1026 return Fail;
1027 }
1028
1029 /* read the function */
1030 return READ_AS_FUNC();
1031 }
1032
1033
1034 /****************************************************************************
1035 **
1036 *F FuncREAD_GAP_ROOT( <self>, <filename> ) . . . . . . . . . . . read a file
1037 */
FuncREAD_GAP_ROOT(Obj self,Obj filename)1038 static Obj FuncREAD_GAP_ROOT(Obj self, Obj filename)
1039 {
1040 Char filenamecpy[GAP_PATH_MAX];
1041
1042 // check the argument
1043 RequireStringRep("READ", filename);
1044
1045 /* Copy to avoid garbage collection moving string */
1046 strlcpy(filenamecpy, CONST_CSTR_STRING(filename), GAP_PATH_MAX);
1047 /* try to open the file */
1048 return READ_GAP_ROOT(filenamecpy) ? True : False;
1049 }
1050
1051
1052 /****************************************************************************
1053 **
1054 *F FuncTmpName( <self> ) . . . . . . . . . . . . . . return a temporary name
1055 */
FuncTmpName(Obj self)1056 static Obj FuncTmpName(Obj self)
1057 {
1058 Char * tmp;
1059 Obj name;
1060
1061 tmp = SyTmpname();
1062 if ( tmp == 0 )
1063 return Fail;
1064 name = MakeString(tmp);
1065 return name;
1066 }
1067
1068
1069 /****************************************************************************
1070 **
1071 *F FuncTmpDirectory( <self> ) . . . . . . . . return a temporary directory
1072 */
FuncTmpDirectory(Obj self)1073 static Obj FuncTmpDirectory(Obj self)
1074 {
1075 Char * tmp;
1076 Obj name;
1077
1078 tmp = SyTmpdir("tm");
1079 if ( tmp == 0 )
1080 return Fail;
1081 name = MakeString(tmp);
1082 return name;
1083 }
1084
1085
1086 /****************************************************************************
1087 **
1088 *F FuncRemoveFile( <self>, <name> ) . . . . . . . . . . remove file <name>
1089 */
FuncRemoveFile(Obj self,Obj filename)1090 static Obj FuncRemoveFile(Obj self, Obj filename)
1091 {
1092 // check the argument
1093 RequireStringRep("RemoveFile", filename);
1094
1095 /* call the system dependent function */
1096 return SyRemoveFile( CONST_CSTR_STRING(filename) ) == -1 ? Fail : True;
1097 }
1098
1099 /****************************************************************************
1100 **
1101 *F FuncCreateDir( <self>, <name> ) . . . . . . . . . . . . create directory
1102 */
FuncCreateDir(Obj self,Obj filename)1103 static Obj FuncCreateDir(Obj self, Obj filename)
1104 {
1105 // check the argument
1106 RequireStringRep("CreateDir", filename);
1107
1108 /* call the system dependent function */
1109 return SyMkdir( CONST_CSTR_STRING(filename) ) == -1 ? Fail : True;
1110 }
1111
1112 /****************************************************************************
1113 **
1114 *F FuncRemoveDir( <self>, <name> ) . . . . . . . . . . . . remove directory
1115 */
FuncRemoveDir(Obj self,Obj filename)1116 static Obj FuncRemoveDir(Obj self, Obj filename)
1117 {
1118 // check the argument
1119 RequireStringRep("RemoveDir", filename);
1120
1121 /* call the system dependent function */
1122 return SyRmdir( CONST_CSTR_STRING(filename) ) == -1 ? Fail : True;
1123 }
1124
1125 /****************************************************************************
1126 **
1127 *F FuncIsDir( <self>, <name> ) . . . . . check whether something is a dir
1128 */
FuncIsDir(Obj self,Obj filename)1129 static Obj FuncIsDir(Obj self, Obj filename)
1130 {
1131 RequireStringRep("IsDir", filename);
1132
1133 /* call the system dependent function */
1134 return SyIsDir( CONST_CSTR_STRING(filename) );
1135 }
1136
1137
1138
1139
1140 /****************************************************************************
1141 **
1142 *F * * * * * * * * * * * file access test functions * * * * * * * * * * * * *
1143 */
1144
1145
1146 /****************************************************************************
1147 **
1148 *F FuncLastSystemError( <self> ) . . . . . . . return the last system error
1149 */
1150 static UInt ErrorMessageRNam;
1151 static UInt ErrorNumberRNam;
1152
FuncLastSystemError(Obj self)1153 static Obj FuncLastSystemError(Obj self)
1154 {
1155 Obj err;
1156 Obj msg;
1157
1158 /* constructed an error record */
1159 err = NEW_PREC(0);
1160
1161 /* check if an errors has occured */
1162 if ( SyLastErrorNo != 0 ) {
1163 ASS_REC( err, ErrorNumberRNam, INTOBJ_INT(SyLastErrorNo) );
1164 msg = MakeString(SyLastErrorMessage);
1165 ASS_REC( err, ErrorMessageRNam, msg );
1166 }
1167
1168 /* no error has occured */
1169 else {
1170 ASS_REC( err, ErrorNumberRNam, INTOBJ_INT(0) );
1171 msg = MakeString("no error");
1172 ASS_REC( err, ErrorMessageRNam, msg );
1173 }
1174
1175 /* return the error record */
1176 return err;
1177 }
1178
1179
1180 /****************************************************************************
1181 **
1182 *F FuncIsExistingFile( <self>, <name> ) . . . . . . does file <name> exists
1183 */
FuncIsExistingFile(Obj self,Obj filename)1184 static Obj FuncIsExistingFile(Obj self, Obj filename)
1185 {
1186 Int res;
1187
1188 // check the argument
1189 RequireStringRep("IsExistingFile", filename);
1190
1191 /* call the system dependent function */
1192 res = SyIsExistingFile( CONST_CSTR_STRING(filename) );
1193 return res == -1 ? False : True;
1194 }
1195
1196
1197 /****************************************************************************
1198 **
1199 *F FuncIsReadableFile( <self>, <name> ) . . . . . . is file <name> readable
1200 */
FuncIsReadableFile(Obj self,Obj filename)1201 static Obj FuncIsReadableFile(Obj self, Obj filename)
1202 {
1203 Int res;
1204
1205 // check the argument
1206 RequireStringRep("IsReadableFile", filename);
1207
1208 /* call the system dependent function */
1209 res = SyIsReadableFile( CONST_CSTR_STRING(filename) );
1210 return res == -1 ? False : True;
1211 }
1212
1213
1214 /****************************************************************************
1215 **
1216 *F FuncIsWritableFile( <self>, <name> ) . . . . . . is file <name> writable
1217 */
FuncIsWritableFile(Obj self,Obj filename)1218 static Obj FuncIsWritableFile(Obj self, Obj filename)
1219 {
1220 Int res;
1221
1222 // check the argument
1223 RequireStringRep("IsWritableFile", filename);
1224
1225 /* call the system dependent function */
1226 res = SyIsWritableFile( CONST_CSTR_STRING(filename) );
1227 return res == -1 ? False : True;
1228 }
1229
1230
1231 /****************************************************************************
1232 **
1233 *F FuncIsExecutableFile( <self>, <name> ) . . . . is file <name> executable
1234 */
FuncIsExecutableFile(Obj self,Obj filename)1235 static Obj FuncIsExecutableFile(Obj self, Obj filename)
1236 {
1237 Int res;
1238
1239 // check the argument
1240 RequireStringRep("IsExecutableFile", filename);
1241
1242 /* call the system dependent function */
1243 res = SyIsExecutableFile( CONST_CSTR_STRING(filename) );
1244 return res == -1 ? False : True;
1245 }
1246
1247
1248 /****************************************************************************
1249 **
1250 *F FuncIsDirectoryPath( <self>, <name> ) . . . . is file <name> a directory
1251 */
FuncIsDirectoryPathString(Obj self,Obj filename)1252 static Obj FuncIsDirectoryPathString(Obj self, Obj filename)
1253 {
1254 Int res;
1255
1256 // check the argument
1257 RequireStringRep("IsDirectoryPathString", filename);
1258
1259 /* call the system dependent function */
1260 res = SyIsDirectoryPath( CONST_CSTR_STRING(filename) );
1261 return res == -1 ? False : True;
1262 }
1263
1264
1265 /****************************************************************************
1266 **
1267 *F FuncLIST_DIR( <self>, <dirname> ) . . . read names of files in dir
1268 **
1269 ** This function returns a GAP list which contains the names of all files
1270 ** contained in a directory <dirname>.
1271 **
1272 ** If <dirname> could not be opened as a directory 'fail' is returned. The
1273 ** reason for the error can be found with 'LastSystemError();' in GAP.
1274 **
1275 */
FuncLIST_DIR(Obj self,Obj dirname)1276 static Obj FuncLIST_DIR(Obj self, Obj dirname)
1277 {
1278 DIR *dir;
1279 struct dirent *entry;
1280 Obj res;
1281
1282 // check the argument
1283 RequireStringRep("LIST_DIR", dirname);
1284
1285 SyClearErrorNo();
1286 dir = opendir(CONST_CSTR_STRING(dirname));
1287 if (dir == NULL) {
1288 SySetErrorNo();
1289 return Fail;
1290 }
1291 res = NEW_PLIST(T_PLIST, 16);
1292 while ((entry = readdir(dir))) {
1293 PushPlist(res, MakeImmString(entry->d_name));
1294 }
1295 closedir(dir);
1296 return res;
1297 }
1298
1299 /****************************************************************************
1300 **
1301 *F * * * * * * * * * * * * text stream functions * * * * * * * * * * * * * *
1302 */
1303
1304 /****************************************************************************
1305 **
1306 *F FuncCLOSE_FILE( <self>, <fid> ) . . . . . . . . . . . . . close a stream
1307 */
FuncCLOSE_FILE(Obj self,Obj fid)1308 static Obj FuncCLOSE_FILE(Obj self, Obj fid)
1309 {
1310 // check the argument
1311 Int ifid = GetSmallInt("CLOSE_FILE", fid);
1312
1313 /* call the system dependent function */
1314 Int ret = SyFclose( ifid );
1315 return ret == -1 ? Fail : True;
1316 }
1317
1318
1319 /****************************************************************************
1320 **
1321 *F FuncINPUT_TEXT_FILE( <self>, <name> ) . . . . . . . . . . . open a stream
1322 */
FuncINPUT_TEXT_FILE(Obj self,Obj filename)1323 static Obj FuncINPUT_TEXT_FILE(Obj self, Obj filename)
1324 {
1325 Int fid;
1326
1327 // check the argument
1328 RequireStringRep("INPUT_TEXT_FILE", filename);
1329
1330 /* call the system dependent function */
1331 SyClearErrorNo();
1332 fid = SyFopen( CONST_CSTR_STRING(filename), "r" );
1333 if ( fid == - 1)
1334 SySetErrorNo();
1335 return fid == -1 ? Fail : INTOBJ_INT(fid);
1336 }
1337
1338
1339 /****************************************************************************
1340 **
1341 *F FuncIS_END_OF_FILE( <self>, <fid> ) . . . . . . . . . . . is end of file
1342 */
FuncIS_END_OF_FILE(Obj self,Obj fid)1343 static Obj FuncIS_END_OF_FILE(Obj self, Obj fid)
1344 {
1345 // check the argument
1346 Int ifid = GetSmallInt("IS_END_OF_FILE", fid);
1347
1348 Int ret = SyIsEndOfFile( ifid );
1349 return ret == -1 ? Fail : ( ret == 0 ? False : True );
1350 }
1351
1352
1353 /****************************************************************************
1354 **
1355 *F FuncOUTPUT_TEXT_FILE( <self>, <name>, <append> ) . . . . . open a stream
1356 */
FuncOUTPUT_TEXT_FILE(Obj self,Obj filename,Obj append)1357 static Obj FuncOUTPUT_TEXT_FILE(Obj self, Obj filename, Obj append)
1358 {
1359 Int fid;
1360
1361 // check the argument
1362 RequireStringRep("OUTPUT_TEXT_FILE", filename);
1363 RequireTrueOrFalse("OUTPUT_TEXT_FILE", append);
1364
1365 /* call the system dependent function */
1366 SyClearErrorNo();
1367 if ( append == True ) {
1368 fid = SyFopen( CONST_CSTR_STRING(filename), "a" );
1369 }
1370 else {
1371 fid = SyFopen( CONST_CSTR_STRING(filename), "w" );
1372 }
1373 if ( fid == - 1)
1374 SySetErrorNo();
1375 return fid == -1 ? Fail : INTOBJ_INT(fid);
1376 }
1377
1378
1379 /****************************************************************************
1380 **
1381 *F FuncPOSITION_FILE( <self>, <fid> ) . . . . . . . . . position of stream
1382 */
FuncPOSITION_FILE(Obj self,Obj fid)1383 static Obj FuncPOSITION_FILE(Obj self, Obj fid)
1384 {
1385 // check the argument
1386 Int ifid = GetSmallInt("POSITION_FILE", fid);
1387
1388 Int ret = SyFtell(ifid);
1389
1390 // Return if failed
1391 if (ret == -1) {
1392 return Fail;
1393 }
1394
1395 return INTOBJ_INT(ret);
1396 }
1397
1398
1399
1400 /****************************************************************************
1401 **
1402 *F FuncREAD_BYTE_FILE( <self>, <fid> ) . . . . . . . . . . . . . read a byte
1403 */
FuncREAD_BYTE_FILE(Obj self,Obj fid)1404 static Obj FuncREAD_BYTE_FILE(Obj self, Obj fid)
1405 {
1406 // check the argument
1407 Int ifid = GetSmallInt("READ_BYTE_FILE", fid);
1408
1409 /* call the system dependent function */
1410 Int ret = SyGetch( ifid );
1411
1412 return ret == EOF ? Fail : INTOBJ_INT(ret);
1413 }
1414
1415
1416 /****************************************************************************
1417 **
1418 *F FuncREAD_LINE_FILE( <self>, <fid> ) . . . . . . . . . . . . . read a line
1419 **
1420 ** This uses fgets and works only if there are no zero characters in <fid>.
1421 */
FuncREAD_LINE_FILE(Obj self,Obj fid)1422 static Obj FuncREAD_LINE_FILE(Obj self, Obj fid)
1423 {
1424 Char buf[256];
1425 Char * cstr;
1426 Int len, buflen;
1427 UInt lstr;
1428 Obj str;
1429
1430 // check the argument
1431 Int ifid = GetSmallInt("READ_LINE_FILE", fid);
1432
1433 /* read <fid> until we see a newline or eof or we've read at least
1434 one byte and more are not immediately available */
1435 str = NEW_STRING(0);
1436 len = 0;
1437 while (1) {
1438 if ( len > 0 && !HasAvailableBytes(ifid))
1439 break;
1440 len += 255;
1441 GROW_STRING( str, len );
1442 if ( SyFgetsSemiBlock( buf, 256, ifid ) == 0 )
1443 break;
1444 buflen = strlen(buf);
1445 lstr = GET_LEN_STRING(str);
1446 cstr = CSTR_STRING(str) + lstr;
1447 memcpy( cstr, buf, buflen+1 );
1448 SET_LEN_STRING(str, lstr+buflen);
1449 if ( buf[buflen-1] == '\n' )
1450 break;
1451 }
1452
1453 /* fix the length of <str> */
1454 len = GET_LEN_STRING(str);
1455 ResizeBag( str, SIZEBAG_STRINGLEN(len) );
1456
1457 /* and return */
1458 return len == 0 ? Fail : str;
1459 }
1460
1461 /****************************************************************************
1462 **
1463 *F FuncREAD_ALL_FILE( <self>, <fid>, <limit> ) . . . . . . . read remainder
1464 **
1465 ** more precisely, read until either
1466 ** (a) we have read at least one byte and no more are available
1467 ** (b) we have evidence that it will never be possible to read a byte
1468 ** (c) we have read <limit> bytes (-1 indicates no limit)
1469 */
1470
FuncREAD_ALL_FILE(Obj self,Obj fid,Obj limit)1471 static Obj FuncREAD_ALL_FILE(Obj self, Obj fid, Obj limit)
1472 {
1473 Char buf[20000];
1474 Int len;
1475 // Length of string read this loop (or negative for error)
1476 UInt lstr;
1477 Obj str;
1478 UInt csize;
1479
1480 // check the argument
1481 Int ifid = GetSmallInt("READ_ALL_FILE", fid);
1482
1483 Int ilim = GetSmallInt("READ_ALL_FILE", limit);
1484
1485 /* read <fid> until we see eof or we've read at least
1486 one byte and more are not immediately available */
1487 str = NEW_STRING(0);
1488 len = 0;
1489 lstr = 0;
1490
1491 #ifdef SYS_IS_CYGWIN32
1492 getmore:
1493 #endif
1494 while (ilim == -1 || len < ilim ) {
1495 lstr = 0;
1496 if ( len > 0 && !HasAvailableBytes(ifid))
1497 break;
1498 if (SyBufIsTTY(ifid)) {
1499 if (ilim == -1) {
1500 Pr("#W Warning -- reading to end of input tty will never "
1501 "end\n",
1502 0, 0);
1503 csize = 20000;
1504 }
1505 else
1506 csize = ((ilim - len) > 20000) ? 20000 : ilim - len;
1507
1508 if (SyFgetsSemiBlock(buf, csize, ifid))
1509 lstr = strlen(buf);
1510 else
1511 lstr = 0;
1512 }
1513 else {
1514 do {
1515 csize =
1516 (ilim == -1 || (ilim - len) > 20000) ? 20000 : ilim - len;
1517 lstr = SyReadWithBuffer(ifid, buf, csize);
1518 } while (lstr == -1 && errno == EAGAIN);
1519 }
1520 if (lstr <= 0) {
1521 SyBufSetEOF(ifid);
1522 break;
1523 }
1524 GROW_STRING( str, len+lstr );
1525 memcpy(CHARS_STRING(str)+len, buf, lstr);
1526 len += lstr;
1527 SET_LEN_STRING(str, len);
1528 }
1529
1530 /* fix the length of <str> */
1531 len = GET_LEN_STRING(str);
1532 #ifdef SYS_IS_CYGWIN32
1533 /* line end hackery */
1534 UInt i = 0, j = 0;
1535 while (i < len) {
1536 if (CHARS_STRING(str)[i] == '\r') {
1537 if (i < len - 1 && CHARS_STRING(str)[i + 1] == '\n') {
1538 i++;
1539 continue;
1540 }
1541 else
1542 CHARS_STRING(str)[i] = '\n';
1543 }
1544 CHARS_STRING(str)[j++] = CHARS_STRING(str)[i++];
1545 }
1546 len = j;
1547 SET_LEN_STRING(str, len);
1548 // If we have not yet read enough, and have read at least one character this loop, then read more
1549 if (ilim != -1 && len < ilim && lstr > 0)
1550 goto getmore;
1551 #endif
1552 ResizeBag( str, SIZEBAG_STRINGLEN(len) );
1553
1554 /* and return */
1555 return len == 0 ? Fail : str;
1556 }
1557
1558 /****************************************************************************
1559 **
1560 *F FuncSEEK_POSITION_FILE( <self>, <fid>, <pos> ) . seek position of stream
1561 */
FuncSEEK_POSITION_FILE(Obj self,Obj fid,Obj pos)1562 static Obj FuncSEEK_POSITION_FILE(Obj self, Obj fid, Obj pos)
1563 {
1564 Int ret;
1565
1566 // check the argument
1567 Int ifid = GetSmallInt("SEEK_POSITION_FILE", fid);
1568 Int ipos = GetSmallInt("SEEK_POSITION_FILE", pos);
1569
1570 ret = SyFseek( ifid, ipos );
1571 return ret == -1 ? Fail : True;
1572 }
1573
1574
1575 /****************************************************************************
1576 **
1577 *F FuncWRITE_BYTE_FILE( <self>, <fid>, <byte> ) . . . . . . . write a byte
1578 */
FuncWRITE_BYTE_FILE(Obj self,Obj fid,Obj ch)1579 static Obj FuncWRITE_BYTE_FILE(Obj self, Obj fid, Obj ch)
1580 {
1581 // check the argument
1582 Int ifid = GetSmallInt("WRITE_BYTE_FILE", fid);
1583 Int ich = GetSmallInt("WRITE_BYTE_FILE", ch);
1584
1585 /* call the system dependent function */
1586 Int ret = SyEchoch( ich, ifid );
1587 return ret == -1 ? Fail : True;
1588 }
1589
1590 /****************************************************************************
1591 **
1592 *F FuncWRITE_STRING_FILE_NC( <self>, <fid>, <string> ) .write a whole string
1593 */
FuncWRITE_STRING_FILE_NC(Obj self,Obj fid,Obj str)1594 static Obj FuncWRITE_STRING_FILE_NC(Obj self, Obj fid, Obj str)
1595 {
1596 Int len = 0, l, ret;
1597 const char *ptr;
1598
1599 /* don't check the argument */
1600
1601 len = GET_LEN_STRING(str);
1602 ptr = CONST_CSTR_STRING(str);
1603 while (len > 0) {
1604 l = (len > 1048576) ? 1048576 : len;
1605 ret = SyWrite(INT_INTOBJ(fid), ptr, l);
1606 if (ret == -1) {
1607 SySetErrorNo();
1608 return Fail;
1609 }
1610 len -= ret;
1611 ptr += ret;
1612 }
1613 return True;
1614 }
1615
FuncREAD_STRING_FILE(Obj self,Obj fid)1616 static Obj FuncREAD_STRING_FILE(Obj self, Obj fid)
1617 {
1618 // check the argument
1619 Int ifid = GetSmallInt("READ_STRING_FILE", fid);
1620 return SyReadStringFid(ifid);
1621 }
1622
1623 /****************************************************************************
1624 **
1625 *F FuncFD_OF_FILE( <fid> )
1626 */
FuncFD_OF_FILE(Obj self,Obj fid)1627 static Obj FuncFD_OF_FILE(Obj self, Obj fid)
1628 {
1629 Int fd = GetSmallInt("FD_OF_FILE", fid);
1630 Int fdi = SyBufFileno(fd);
1631 return INTOBJ_INT(fdi);
1632 }
1633
1634 #ifdef HPCGAP
FuncRAW_MODE_FILE(Obj self,Obj fid,Obj onoff)1635 static Obj FuncRAW_MODE_FILE(Obj self, Obj fid, Obj onoff)
1636 {
1637 Int fd = GetSmallInt("RAW_MODE_FILE", fid);
1638 if (onoff == False || onoff == Fail) {
1639 syStopraw(fd);
1640 return False;
1641 }
1642 else
1643 return syStartraw(fd) ? True : False;
1644 }
1645 #endif
1646
1647 #ifdef HAVE_SELECT
FuncUNIXSelect(Obj self,Obj inlist,Obj outlist,Obj exclist,Obj timeoutsec,Obj timeoutusec)1648 static Obj FuncUNIXSelect(Obj self,
1649 Obj inlist,
1650 Obj outlist,
1651 Obj exclist,
1652 Obj timeoutsec,
1653 Obj timeoutusec)
1654 {
1655 fd_set infds,outfds,excfds;
1656 struct timeval tv;
1657 int n,maxfd;
1658 Int i,j;
1659 Obj o;
1660
1661 RequirePlainList("UNIXSelect", inlist);
1662 RequirePlainList("UNIXSelect", outlist);
1663 RequirePlainList("UNIXSelect", exclist);
1664
1665 FD_ZERO(&infds);
1666 FD_ZERO(&outfds);
1667 FD_ZERO(&excfds);
1668 maxfd = 0;
1669 /* Handle input file descriptors: */
1670 for (i = 1;i <= LEN_PLIST(inlist);i++) {
1671 o = ELM_PLIST(inlist,i);
1672 if (o != (Obj) 0 && IS_INTOBJ(o)) {
1673 j = INT_INTOBJ(o); /* a UNIX file descriptor */
1674 FD_SET(j,&infds);
1675 if (j > maxfd) maxfd = j;
1676 }
1677 }
1678 /* Handle output file descriptors: */
1679 for (i = 1;i <= LEN_PLIST(outlist);i++) {
1680 o = ELM_PLIST(outlist,i);
1681 if (o != (Obj) 0 && IS_INTOBJ(o)) {
1682 j = INT_INTOBJ(o); /* a UNIX file descriptor */
1683 FD_SET(j,&outfds);
1684 if (j > maxfd) maxfd = j;
1685 }
1686 }
1687 /* Handle exception file descriptors: */
1688 for (i = 1;i <= LEN_PLIST(exclist);i++) {
1689 o = ELM_PLIST(exclist,i);
1690 if (o != (Obj) 0 && IS_INTOBJ(o)) {
1691 j = INT_INTOBJ(o); /* a UNIX file descriptor */
1692 FD_SET(j,&excfds);
1693 if (j > maxfd) maxfd = j;
1694 }
1695 }
1696 /* Handle the timeout: */
1697 if (timeoutsec != (Obj) 0 && IS_INTOBJ(timeoutsec) &&
1698 timeoutusec != (Obj) 0 && IS_INTOBJ(timeoutusec)) {
1699 tv.tv_sec = INT_INTOBJ(timeoutsec);
1700 tv.tv_usec = INT_INTOBJ(timeoutusec);
1701 n = select(maxfd+1,&infds,&outfds,&excfds,&tv);
1702 } else {
1703 n = select(maxfd+1,&infds,&outfds,&excfds,NULL);
1704 }
1705
1706 if (n >= 0) {
1707 /* Now run through the lists and call functions if ready: */
1708
1709 for (i = 1;i <= LEN_PLIST(inlist);i++) {
1710 o = ELM_PLIST(inlist,i);
1711 if (o != (Obj) 0 && IS_INTOBJ(o)) {
1712 j = INT_INTOBJ(o); /* a UNIX file descriptor */
1713 if (!(FD_ISSET(j,&infds))) {
1714 SET_ELM_PLIST(inlist,i,Fail);
1715 CHANGED_BAG(inlist);
1716 }
1717 }
1718 }
1719 /* Handle output file descriptors: */
1720 for (i = 1;i <= LEN_PLIST(outlist);i++) {
1721 o = ELM_PLIST(outlist,i);
1722 if (o != (Obj) 0 && IS_INTOBJ(o)) {
1723 j = INT_INTOBJ(o); /* a UNIX file descriptor */
1724 if (!(FD_ISSET(j,&outfds))) {
1725 SET_ELM_PLIST(outlist,i,Fail);
1726 CHANGED_BAG(outlist);
1727 }
1728 }
1729 }
1730 /* Handle exception file descriptors: */
1731 for (i = 1;i <= LEN_PLIST(exclist);i++) {
1732 o = ELM_PLIST(exclist,i);
1733 if (o != (Obj) 0 && IS_INTOBJ(o)) {
1734 j = INT_INTOBJ(o); /* a UNIX file descriptor */
1735 if (!(FD_ISSET(j,&excfds))) {
1736 SET_ELM_PLIST(exclist,i,Fail);
1737 CHANGED_BAG(exclist);
1738 }
1739 }
1740 }
1741 }
1742 return INTOBJ_INT(n);
1743 }
1744 #endif
1745
1746 /****************************************************************************
1747 **
1748 *F * * * * * * * * * * * * * execution functions * * * * * * * * * * * * * *
1749 */
1750
1751
1752 /****************************************************************************
1753 **
1754 *F FuncExecuteProcess( <self>, <dir>, <prg>, <in>, <out>, <args> ) process
1755 */
1756 static Obj
FuncExecuteProcess(Obj self,Obj dir,Obj prg,Obj in,Obj out,Obj args)1757 FuncExecuteProcess(Obj self, Obj dir, Obj prg, Obj in, Obj out, Obj args)
1758 {
1759 Obj ExecArgs[1024];
1760 Char * ExecCArgs[1024];
1761
1762 Obj tmp;
1763 Int res;
1764 Int i;
1765
1766 // check the argument
1767 RequireStringRep("ExecuteProcess", dir);
1768 RequireStringRep("ExecuteProcess", prg);
1769 Int iin = GetSmallInt("ExecuteProcess", in);
1770 Int iout = GetSmallInt("ExecuteProcess", out);
1771 RequireSmallList("ExecuteProcess", args);
1772
1773 /* create an argument array */
1774 for ( i = 1; i <= LEN_LIST(args); i++ ) {
1775 if ( i == 1023 )
1776 break;
1777 tmp = ELM_LIST( args, i );
1778 RequireStringRep("ExecuteProcess", tmp);
1779 ExecArgs[i] = tmp;
1780 }
1781 ExecCArgs[0] = CSTR_STRING(prg);
1782 ExecCArgs[i] = 0;
1783 for ( i--; 0 < i; i-- ) {
1784 ExecCArgs[i] = CSTR_STRING(ExecArgs[i]);
1785 }
1786 if (SyWindow && out == INTOBJ_INT(1)) /* standard output */
1787 syWinPut( INT_INTOBJ(out), "@z","");
1788
1789 /* execute the process */
1790 res = SyExecuteProcess( CSTR_STRING(dir),
1791 CSTR_STRING(prg),
1792 iin,
1793 iout,
1794 ExecCArgs );
1795
1796 if (SyWindow && out == INTOBJ_INT(1)) /* standard output */
1797 syWinPut( INT_INTOBJ(out), "@mAgIc","");
1798 return res == 255 ? Fail : INTOBJ_INT(res);
1799 }
1800
1801
1802 /****************************************************************************
1803 **
1804 *F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
1805 */
1806
1807 /****************************************************************************
1808 **
1809 *V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
1810 */
1811 static StructGVarFunc GVarFuncs[] = {
1812
1813 GVAR_FUNC(READ, 1, "filename"),
1814 GVAR_FUNC(READ_NORECOVERY, 1, "filename"),
1815 GVAR_FUNC(READ_ALL_COMMANDS, 4, "instream, echo, capture, outputFunc"),
1816 GVAR_FUNC(READ_COMMAND_REAL, 2, "stream, echo"),
1817 GVAR_FUNC(READ_STREAM, 1, "stream"),
1818 GVAR_FUNC(READ_STREAM_LOOP, 2, "stream, catchstderrout"),
1819 GVAR_FUNC(READ_STREAM_LOOP_WITH_CONTEXT, 3, "stream, catchstderrout, context"),
1820 GVAR_FUNC(READ_AS_FUNC, 1, "filename"),
1821 GVAR_FUNC(READ_AS_FUNC_STREAM, 1, "stream"),
1822 GVAR_FUNC(READ_GAP_ROOT, 1, "filename"),
1823 GVAR_FUNC(LOG_TO, 1, "filename"),
1824 GVAR_FUNC(LOG_TO_STREAM, 1, "filename"),
1825 GVAR_FUNC(CLOSE_LOG_TO, 0, ""),
1826 GVAR_FUNC(INPUT_LOG_TO, 1, "filename"),
1827 GVAR_FUNC(INPUT_LOG_TO_STREAM, 1, "filename"),
1828 GVAR_FUNC(CLOSE_INPUT_LOG_TO, 0, ""),
1829 GVAR_FUNC(OUTPUT_LOG_TO, 1, "filename"),
1830 GVAR_FUNC(OUTPUT_LOG_TO_STREAM, 1, "filename"),
1831 GVAR_FUNC(CLOSE_OUTPUT_LOG_TO, 0, ""),
1832 GVAR_FUNC(Print, -1, "args"),
1833 GVAR_FUNC(PRINT_TO, -1, "args"),
1834 GVAR_FUNC(PRINT_TO_STREAM, -1, "args"),
1835 GVAR_FUNC(APPEND_TO, -1, "args"),
1836 GVAR_FUNC(APPEND_TO_STREAM, -1, "args"),
1837 GVAR_FUNC(TmpName, 0, ""),
1838 GVAR_FUNC(TmpDirectory, 0, ""),
1839 GVAR_FUNC(RemoveFile, 1, "filename"),
1840 GVAR_FUNC(CreateDir, 1, "filename"),
1841 GVAR_FUNC(RemoveDir, 1, "filename"),
1842 GVAR_FUNC(IsDir, 1, "filename"),
1843 GVAR_FUNC(LastSystemError, 0, ""),
1844 GVAR_FUNC(IsExistingFile, 1, "filename"),
1845 GVAR_FUNC(IsReadableFile, 1, "filename"),
1846 GVAR_FUNC(IsWritableFile, 1, "filename"),
1847 GVAR_FUNC(IsExecutableFile, 1, "filename"),
1848 GVAR_FUNC(IsDirectoryPathString, 1, "filename"),
1849 GVAR_FUNC(LIST_DIR, 1, "dirname"),
1850 GVAR_FUNC(CLOSE_FILE, 1, "fid"),
1851 GVAR_FUNC(INPUT_TEXT_FILE, 1, "filename"),
1852 GVAR_FUNC(OUTPUT_TEXT_FILE, 2, "filename, append"),
1853 GVAR_FUNC(IS_END_OF_FILE, 1, "fid"),
1854 GVAR_FUNC(POSITION_FILE, 1, "fid"),
1855 GVAR_FUNC(READ_BYTE_FILE, 1, "fid"),
1856 GVAR_FUNC(READ_LINE_FILE, 1, "fid"),
1857 GVAR_FUNC(READ_ALL_FILE, 2, "fid, limit"),
1858 GVAR_FUNC(SEEK_POSITION_FILE, 2, "fid, pos"),
1859 GVAR_FUNC(WRITE_BYTE_FILE, 2, "fid, byte"),
1860 GVAR_FUNC(WRITE_STRING_FILE_NC, 2, "fid, string"),
1861 GVAR_FUNC(READ_STRING_FILE, 1, "fid"),
1862 GVAR_FUNC(FD_OF_FILE, 1, "fid"),
1863 #ifdef HPCGAP
1864 GVAR_FUNC(RAW_MODE_FILE, 2, "fid, bool"),
1865 #endif
1866 #ifdef HAVE_SELECT
1867 GVAR_FUNC(
1868 UNIXSelect, 5, "inlist, outlist, exclist, timeoutsec, timeoutusec"),
1869 #endif
1870 GVAR_FUNC(ExecuteProcess, 5, "dir, prg, in, out, args"),
1871 { 0, 0, 0, 0, 0 }
1872
1873 };
1874
1875
1876 /****************************************************************************
1877 **
1878 *F InitKernel( <module> ) . . . . . . . . initialise kernel data structures
1879 */
InitKernel(StructInitInfo * module)1880 static Int InitKernel (
1881 StructInitInfo * module )
1882 {
1883 /* init filters and functions */
1884 InitHdlrFuncsFromTable( GVarFuncs );
1885
1886 ImportFuncFromLibrary( "IsInputStream", &IsInputStream );
1887 ImportFuncFromLibrary( "IsOutputStream", &IsOutputStream );
1888
1889 /* return success */
1890 return 0;
1891 }
1892
1893
1894 /****************************************************************************
1895 **
1896 *F PostRestore( <module> ) . . . . . . . . . . . . . after restore workspace
1897 */
PostRestore(StructInitInfo * module)1898 static Int PostRestore (
1899 StructInitInfo * module )
1900 {
1901 /* file access test functions */
1902 ErrorNumberRNam = RNamName("number");
1903 ErrorMessageRNam = RNamName("message");
1904
1905 /* pick up the number of this global */
1906 LastReadValueGVar = GVarName("LastReadValue");
1907
1908 /* return success */
1909 return 0;
1910 }
1911
1912
1913 /****************************************************************************
1914 **
1915 *F InitLibrary( <module> ) . . . . . . . initialise library data structures
1916 */
InitLibrary(StructInitInfo * module)1917 static Int InitLibrary (
1918 StructInitInfo * module )
1919 {
1920 /* init filters and functions */
1921 InitGVarFuncsFromTable( GVarFuncs );
1922
1923
1924 /* return success */
1925 return PostRestore( module );
1926 }
1927
1928
1929 /****************************************************************************
1930 **
1931 *F InitInfoStreams() . . . . . . . . . . . . . . . . table of init functions
1932 */
1933 static StructInitInfo module = {
1934 // init struct using C99 designated initializers; for a full list of
1935 // fields, please refer to the definition of StructInitInfo
1936 .type = MODULE_BUILTIN,
1937 .name = "streams",
1938 .initKernel = InitKernel,
1939 .initLibrary = InitLibrary,
1940 .postRestore = PostRestore,
1941 };
1942
InitInfoStreams(void)1943 StructInitInfo * InitInfoStreams ( void )
1944 {
1945 return &module;
1946 }
1947