/**************************************************************************** ** ** This file is part of GAP, a system for computational discrete algebra. ** ** Copyright of GAP belongs to its developers, whose names are too numerous ** to list here. Please refer to the COPYRIGHT file for details. ** ** SPDX-License-Identifier: GPL-2.0-or-later ** ** This file contains the various read-eval-print loops and streams related ** stuff. The system depend part is in "sysfiles.c".
*/
static UInt OpenInputFileOrStream(constchar * funcname,
TypInputFile * input,
Obj inputObj)
{ if (IsStringConv(inputObj)) { return OpenInput(input, CONST_CSTR_STRING(inputObj));
} elseif (CALL_1ARGS(IsInputStream, inputObj) == True) { return OpenInputStream(input, inputObj, FALSE);
}
RequireArgumentEx(funcname, inputObj, "", "must be a string or an input stream");
}
/**************************************************************************** ** *F FuncREAD_ALL_COMMANDS( <self>, <instream>, <echo>, <capture>, ** <resultCallback> ) ** ** FuncREAD_ALL_COMMANDS attempts to execute all statements read from the ** stream <instream>. It returns 'fail' if the stream cannot be opened, ** otherwise a list of lists, each entry of which reflects the result of the ** execution of one statement. ** ** If the parameter <echo> is 'true', then the statements are echoed to the ** current output. ** ** If the parameter <capture> is 'true', then any output occurring during ** execution of a statement, including the output of <resultCallback>, is ** captured into a string. ** ** If <resultCallback> is a function, then this function is called on every ** statement result, otherwise this parameter is ignored. Possible outputs of ** this function are captured if <capture> is 'true'. ** ** The results are returned as lists of length at most five, the structure of ** which is explained below: ** ** - The first entry is 'true' if the statement was executed successfully, ** and 'false' otherwise. ** ** - If the first entry is 'true', then the second entry is bound to the ** result of the statement if there was one, and unbound otherwise. ** ** - The third entry is 'true' if the statement ended in a dual semicolon, ** and 'false' otherwise. ** ** - The fourth entry contains the return value of <resultCallback> if ** applicable. ** ** - The fifth entry contains the captured output as a string, if <capture> ** is 'true'. ** ** This function is currently used in interactive tools such as the GAP ** Jupyter kernel to execute cells and is likely to be replaced by a function ** that can read a single command from a stream without losing the rest of ** its content.
*/
Obj READ_ALL_COMMANDS(Obj instream, Obj echo, Obj capture, Obj resultCallback)
{ volatile Obj outstream = 0; volatile Obj outstreamString = 0;
GAP_TRY
{ while (1) { if (outstream) { // Clean in case there has been any output
SET_LEN_STRING(outstreamString, 0);
}
BOOL dualSemicolon;
Obj evalResult;
ExecStatus status = ReadEvalCommand(0, &input, &evalResult, &dualSemicolon); if (status == STATUS_EOF || status == STATUS_QUIT ||
status == STATUS_QQUIT) break;
Obj result = NEW_PLIST(T_PLIST, 5);
AssPlist(result, 1, False);
PushPlist(resultList, result);
/* Returns a list with one or two entries. The first entry is set to "false" if there was any error executing the command, and "true" otherwise. The second entry, if present, is the return value of the command. If it not present, the command returned nothing.
*/ static Obj FuncREAD_COMMAND_REAL(Obj self, Obj stream, Obj echo)
{
Obj result;
Obj evalResult;
RequireInputStream(SELF_NAME, stream);
result = NEW_PLIST(T_PLIST, 2);
AssPlist(result, 1, False);
// open the stream, read a command, and close it again
TypInputFile input; if (!OpenInputStream(&input, stream, echo == True)) { return result;
}
ExecStatus status;
if (status == STATUS_EOF || status == STATUS_QQUIT) return result; elseif (STATE(UserHasQuit) || STATE(UserHasQUIT)) return result; elseif (status == STATUS_RETURN)
Pr("'return' must not be used in file read-eval loop\n", 0, 0);
/**************************************************************************** ** *F READ() . . . . . . . . . . . . . . . . . . . . . . . read current input ** ** Read the current input and close the input stream.
*/
static UInt LastReadValueGVar;
staticvoid READ_INNER(TypInputFile * input)
{ if (STATE(UserHasQuit))
{
Pr("Warning: Entering READ with UserHasQuit set, this should never happen, resetting",0,0);
STATE(UserHasQuit) = FALSE;
} if (STATE(UserHasQUIT))
{
Pr("Warning: Entering READ with UserHasQUIT set, this should never happen, resetting",0,0);
STATE(UserHasQUIT) = FALSE;
}
AssGVarWithoutReadOnlyCheck( LastReadValueGVar, 0);
// now do the reading while ( 1 ) {
Obj evalResult;
ExecStatus status = ReadEvalCommand(0, input, &evalResult, 0); if (STATE(UserHasQuit) || STATE(UserHasQUIT)) break;
// handle return-value or return-void command if (status == STATUS_RETURN) {
Pr("'return' must not be used in file read-eval loop\n", 0, 0);
}
/**************************************************************************** ** *F READ_AS_FUNC() . . . . . . . . . . . . . read current input as function ** ** Read the current input as function and close the input stream.
*/
Obj READ_AS_FUNC(TypInputFile * input)
{ // now do the reading
Obj evalResult;
ExecStatus status = ReadEvalFile(input, &evalResult);
// get the function
Obj func = (status == STATUS_END) ? evalResult : Fail;
// return the function return func;
}
/**************************************************************************** ** *F READ_GAP_ROOT( <filename> ) . . . read from gap root, dyn-load or static ** ** 'READ_GAP_ROOT' tries to find a file under the root directory, it will ** search all directories given in 'SyGapRootPaths', check dynamically ** loadable modules and statically linked modules.
*/ Int READ_GAP_ROOT ( constChar * filename )
{ char path[GAP_PATH_MAX];
// try to find the GAP file
SyFindGapRootFile(filename, path, sizeof(path));
// try to find compiled version of the GAP file if (SyUseModule) { // This code section covers transparently loading GAC compiled // versions of GAP source files, by running code similar to that in // FuncLOAD_STAT. For example, lib/oper1.g is compiled into C code; // when reading lib/oper1.g, we instead load its compiled version. Char module[GAP_PATH_MAX];
strxcpy(module, "GAPROOT/", sizeof(module));
strxcat(module, filename, sizeof(module));
// search for a statically linked module matching the given filename
StructInitInfo * info = LookupStaticModule(module); if (info) { // found a matching statically linked module; if there is also // a GAP file, compare their CRC if (*path && info->crc != SyGAPCRC(path)) {
Pr("#W Static module %s has CRC mismatch, ignoring\n",
(Int)filename, 0);
} else { if (SyDebugLoading) {
Pr("#I READ_GAP_ROOT: loading '%s' statically\n",
(Int)filename, 0);
}
ActivateModule(info);
RecordLoadedModule(info, 1, filename); return 1;
}
}
}
// not found? if (*path == 0) return 0;
#ifdef GAP_ENABLE_SAVELOAD // special handling case if we are trying to load compiled modules needed // for a saved workspace if (SyRestoring) { // ErrorQuit is not available
Pr("Can't find compiled module '%s' needed by saved workspace\n",
(Int)filename, 0); return 0;
} #endif
// ordinary gap file if (SyDebugLoading) {
Pr("#I READ_GAP_ROOT: loading '%s' as GAP file\n", (Int)filename, 0);
}
TypInputFile input; if (!OpenInput(&input, path)) return 0;
GAP_TRY
{ while (1) {
ExecStatus status = ReadEvalCommand(0, &input, 0, 0); if (STATE(UserHasQuit) || STATE(UserHasQUIT)) break; if (status == STATUS_RETURN) {
Pr("'return' must not be used in file", 0, 0);
} elseif (status == STATUS_EOF || status == STATUS_QUIT) { break;
}
}
}
GAP_CATCH
{
CloseInput(&input);
GAP_THROW();
}
CloseInput(&input); return 1;
}
/**************************************************************************** ** *F FuncCALL_WITH_STREAM( <stream>, <func>, <args> ) ** ** Temporarily set the active output stream to <stream>, then call the ** function <func> with the arguments in the list <args>. This can for ** example be used to capture the output of a function into a string.
*/ static Obj FuncCALL_WITH_STREAM(Obj self, Obj stream, Obj func, Obj args)
{
RequireOutputStream(SELF_NAME, stream);
RequireSmallList(SELF_NAME, args);
TypOutputFile output; if (!OpenOutputStream(&output, stream)) {
ErrorQuit("CALL_WITH_STREAM: cannot open stream for output", 0, 0);
}
if (!CloseOutput(&output)) {
ErrorQuit("CALL_WITH_STREAM: cannot close output", 0, 0);
}
return result;
}
/**************************************************************************** ** *F FuncCLOSE_LOG_TO() . . . . . . . . . . . . . . . . . . . . stop logging ** ** 'FuncCLOSE_LOG_TO' implements a method for 'LogTo'. ** ** 'LogTo()' ** ** 'LogTo' called with no argument closes the current logfile again, so that ** input from '*stdin*' and '*errin*' and output to '*stdout*' and ** '*errout*' will no longer be echoed to a file.
*/ static Obj FuncCLOSE_LOG_TO(Obj self)
{ if ( ! CloseLog() ) {
ErrorQuit("LogTo: cannot close the logfile", 0, 0);
} returnTrue;
}
/**************************************************************************** ** *F FuncLOG_TO( <filename> ) . . . . . . . . . . . . start logging to a file ** ** 'FuncLOG_TO' implements a method for 'LogTo' ** ** 'LogTo( <filename> )' ** ** 'LogTo' instructs GAP to echo all input from the standard input files, ** '*stdin*' and '*errin*' and all output to the standard output files, ** '*stdout*' and '*errout*', to the file with the name <filename>. ** The file is created if it does not exist, otherwise it is truncated.
*/ static Obj FuncLOG_TO(Obj self, Obj filename)
{
RequireStringRep(SELF_NAME, filename); if ( ! OpenLog( CONST_CSTR_STRING(filename) ) ) {
ErrorReturnVoid("LogTo: cannot log to %g", (Int)filename, 0, "you can 'return;'"); returnFalse;
} returnTrue;
}
/**************************************************************************** ** *F FuncLOG_TO_STREAM( <stream> ) . . . . . . . . . start logging to a stream
*/ static Obj FuncLOG_TO_STREAM(Obj self, Obj stream)
{
RequireOutputStream(SELF_NAME, stream); if ( ! OpenLogStream(stream) ) {
ErrorReturnVoid("LogTo: cannot log to stream", 0, 0, "you can 'return;'"); returnFalse;
} returnTrue;
}
/**************************************************************************** ** *F FuncCLOSE_INPUT_LOG_TO() . . . . . . . . . . . . . . . . . stop logging ** ** 'FuncCLOSE_INPUT_LOG_TO' implements a method for 'InputLogTo'. ** ** 'InputLogTo()' ** ** 'InputLogTo' called with no argument closes the current logfile again, so ** that input from '*stdin*' and '*errin*' will no longer be echoed to a ** file.
*/ static Obj FuncCLOSE_INPUT_LOG_TO(Obj self)
{ if ( ! CloseInputLog() ) {
ErrorQuit("InputLogTo: cannot close the logfile", 0, 0);
} returnTrue;
}
/**************************************************************************** ** *F FuncINPUT_LOG_TO( <filename> ) . . . . . . . . . start logging to a file ** ** 'FuncINPUT_LOG_TO' implements a method for 'InputLogTo' ** ** 'InputLogTo( <filename> )' ** ** 'InputLogTo' instructs GAP to echo all input from the standard input ** files, '*stdin*' and '*errin*' to the file with the name <filename>. The ** file is created if it does not exist, otherwise it is truncated.
*/ static Obj FuncINPUT_LOG_TO(Obj self, Obj filename)
{
RequireStringRep(SELF_NAME, filename); if ( ! OpenInputLog( CONST_CSTR_STRING(filename) ) ) {
ErrorReturnVoid("InputLogTo: cannot log to %g", (Int)filename, 0, "you can 'return;'"); returnFalse;
} returnTrue;
}
/**************************************************************************** ** *F FuncINPUT_LOG_TO_STREAM( <stream> ) . . . . . . start logging to a stream
*/ static Obj FuncINPUT_LOG_TO_STREAM(Obj self, Obj stream)
{
RequireOutputStream(SELF_NAME, stream); if ( ! OpenInputLogStream(stream) ) {
ErrorReturnVoid("InputLogTo: cannot log to stream", 0, 0, "you can 'return;'"); returnFalse;
} returnTrue;
}
/**************************************************************************** ** *F FuncCLOSE_OUTPUT_LOG_TO() . . . . . . . . . . . . . . . . . stop logging ** ** 'FuncCLOSE_OUTPUT_LOG_TO' implements a method for 'OutputLogTo'. ** ** 'OutputLogTo()' ** ** 'OutputLogTo' called with no argument closes the current logfile again, ** so that output from '*stdin*' and '*errin*' will no longer be echoed to a ** file.
*/ static Obj FuncCLOSE_OUTPUT_LOG_TO(Obj self)
{ if ( ! CloseOutputLog() ) {
ErrorQuit("OutputLogTo: cannot close the logfile", 0, 0);
} returnTrue;
}
/**************************************************************************** ** *F FuncOUTPUT_LOG_TO( <filename> ) . . . . . . . . start logging to a file ** ** 'FuncOUTPUT_LOG_TO' implements a method for 'OutputLogTo' ** ** 'OutputLogTo( <filename> )' ** ** 'OutputLogTo' instructs GAP to echo all output from the standard output ** files, '*stdin*' and '*errin*' to the file with the name <filename>. The ** file is created if it does not exist, otherwise it is truncated.
*/ static Obj FuncOUTPUT_LOG_TO(Obj self, Obj filename)
{
RequireStringRep(SELF_NAME, filename); if ( ! OpenOutputLog( CONST_CSTR_STRING(filename) ) ) {
ErrorReturnVoid("OutputLogTo: cannot log to %g", (Int)filename, 0, "you can 'return;'"); returnFalse;
} returnTrue;
}
/**************************************************************************** ** *F FuncOUTPUT_LOG_TO_STREAM( <stream> ) . . . . . start logging to a stream
*/ static Obj FuncOUTPUT_LOG_TO_STREAM(Obj self, Obj stream)
{
RequireOutputStream(SELF_NAME, stream); if ( ! OpenOutputLogStream(stream) ) {
ErrorReturnVoid("OutputLogTo: cannot log to stream", 0, 0, "you can 'return;'"); returnFalse;
} returnTrue;
}
// first entry is the file or stream
destination = ELM_LIST(args, 1);
TypOutputFile output;
// try to open the output and handle failures if (file) {
RequireStringRep(funcname, destination);
i = OpenOutput(&output, CONST_CSTR_STRING(destination), append); if (!i) { if (streq(CSTR_STRING(destination), "*errout*")) {
Panic("Failed to open *errout*!");
}
ErrorQuit("%s: cannot open '%g' for output", (Int)funcname,
(Int)destination);
}
} else { if (CALL_1ARGS(IsOutputStream, destination) != True) {
ErrorQuit("%s: must be an output stream",
(Int)funcname, 0);
}
i = OpenOutputStream(&output, destination); if (!i) {
ErrorQuit("%s: cannot open stream for output", (Int)funcname, 0);
}
}
// print all the arguments, take care of strings and functions for ( i = 2; i <= LEN_PLIST(args); i++ ) {
arg = ELM_LIST(args,i);
/**************************************************************************** ** *F FuncPRINT_TO_STREAM( <self>, <args> ) . . . . . . . . . . . print <args>
*/ static Obj FuncPRINT_TO_STREAM(Obj self, Obj args)
{ /* Note that FuncPRINT_TO_STREAM and FuncAPPEND_TO_STREAM do exactly the same, they only differ in the function name they print as part
of their error messages. */ return PRINT_OR_APPEND_TO_STREAM(args, 0);
}
/**************************************************************************** ** *F FuncAPPEND_TO_STREAM( <self>, <args> ) . . . . . . . . . . append <args>
*/ static Obj FuncAPPEND_TO_STREAM(Obj self, Obj args)
{ /* Note that FuncPRINT_TO_STREAM and FuncAPPEND_TO_STREAM do exactly the same, they only differ in the function name they print as part
of their error messages. */ return PRINT_OR_APPEND_TO_STREAM(args, 1);
}
/**************************************************************************** ** *F FuncREAD( <self>, <input> ) . . . . . . . . . . . read a file or stream ** ** Read the current input and close the input stream.
*/ static Obj FuncREAD(Obj self, Obj inputObj)
{
TypInputFile input; if (!OpenInputFileOrStream(SELF_NAME, &input, inputObj)) returnFalse;
if (!CloseInput(&input)) {
ErrorQuit("Panic: READ cannot close input", 0, 0);
} returnTrue;
}
/**************************************************************************** ** *F FuncREAD_STREAM_LOOP( <self>, <instream>, <outstream> ) . . read a stream ** ** Read data from <instream> in a read-eval-view loop and write all output ** to <outstream>. This is used by the GAP function `RunTests` and hence ** indirectly for implementing `Test` and `TestDirectory`,
*/ static Obj FuncREAD_STREAM_LOOP(Obj self,
Obj instream,
Obj outstream,
Obj ctx)
{ Int res; volatile Obj context = ctx;
RequireInputStream(SELF_NAME, instream);
RequireOutputStream(SELF_NAME, outstream); if (context == False)
context = 0; elseif (!IS_LVARS_OR_HVARS(context))
RequireArgument(SELF_NAME, context, "must be a local variables bag " "or the value 'false'");
TypInputFile input; if (!OpenInputStream(&input, instream, FALSE)) { returnFalse;
}
TypOutputFile output; if (!OpenOutputStream(&output, outstream)) {
res = CloseInput(&input);
GAP_ASSERT(res); returnFalse;
}
LockCurrentOutput(TRUE);
// save the old print state volatile UInt oldPrintObjState = SetPrintObjState(0);
BOOL rethrow = FALSE;
GAP_TRY
{ // now do the reading while (1) {
Obj evalResult; BOOL dualSemicolon;
UInt oldtime = SyTime();
// read and evaluate the command
SetPrintObjState(0);
ExecStatus status =
ReadEvalCommand(context, &input, &evalResult, &dualSemicolon);
// stop the stopwatch
UpdateTime(oldtime);
// handle ordinary command if (status == STATUS_END && evalResult != 0) {
UpdateLast(evalResult); if (!dualSemicolon) {
ViewObjHandler(evalResult);
}
}
// handle return-value or return-void command elseif (status == STATUS_RETURN) {
Pr("'return' must not be used in file read-eval loop\n", 0, 0);
}
// handle quit command or <end-of-file> elseif (status == STATUS_EOF || status == STATUS_QUIT ||
status == STATUS_QQUIT) { break;
}
}
}
GAP_CATCH
{
rethrow = TRUE;
}
SetPrintObjState(oldPrintObjState);
LockCurrentOutput(FALSE);
res = CloseInput(&input);
res &= CloseOutput(&output);
if (rethrow)
GAP_THROW();
return res ? True : False;
}
/**************************************************************************** ** *F FuncREAD_AS_FUNC( <self>, <input> ) . read a file or stream as a function
*/ static Obj FuncREAD_AS_FUNC(Obj self, Obj inputObj)
{
TypInputFile input; if (!OpenInputFileOrStream(SELF_NAME, &input, inputObj)) returnFalse;
// call the system dependent function
res = SyIsExecutableFile( CONST_CSTR_STRING(filename) ); return res == -1 ? False : True;
}
/**************************************************************************** ** *F FuncIsDirectoryPath( <self>, <name> ) . . . . is file <name> a directory
*/ static Obj FuncIsDirectoryPathString(Obj self, Obj filename)
{ Int res;
RequireStringRep(SELF_NAME, filename);
// call the system dependent function
res = SyIsDirectoryPath( CONST_CSTR_STRING(filename) ); return res == -1 ? False : True;
}
/**************************************************************************** ** *F FuncLIST_DIR( <self>, <dirname> ) . . . read names of files in dir ** ** This function returns a GAP list which contains the names of all files ** contained in a directory <dirname>. ** ** If <dirname> could not be opened as a directory 'fail' is returned. The ** reason for the error can be found with 'LastSystemError();' in GAP. **
*/ static Obj FuncLIST_DIR(Obj self, Obj dirname)
{
DIR *dir; struct dirent *entry;
Obj res;
RequireStringRep(SELF_NAME, dirname);
SyClearErrorNo();
dir = opendir(CONST_CSTR_STRING(dirname)); if (dir == NULL) {
SySetErrorNo(); return Fail;
}
res = NEW_PLIST(T_PLIST, 16); while ((entry = readdir(dir))) {
PushPlist(res, MakeImmString(entry->d_name));
}
closedir(dir); return res;
}
// call the system dependent function Int ret = SyGetch( ifid );
return ret == EOF ? Fail : INTOBJ_INT(ret);
}
/**************************************************************************** ** *F FuncREAD_LINE_FILE( <self>, <fid> ) . . . . . . . . . . . . . read a line ** ** This uses fgets and works only if there are no zero characters in <fid>.
*/ static Obj FuncREAD_LINE_FILE(Obj self, Obj fid)
{ Char buf[256]; Char * cstr; Int len, buflen;
UInt lstr;
Obj str;
Int ifid = GetSmallInt(SELF_NAME, fid);
// read <fid> until we see a newline or eof or we've read at least // one byte and more are not immediately available
str = NEW_STRING(0);
len = 0; while (1) { if ( len > 0 && !HasAvailableBytes(ifid)) break;
len += 255;
GROW_STRING( str, len ); if ( SyFgetsSemiBlock( buf, 256, ifid ) == 0 ) break;
buflen = strlen(buf);
lstr = GET_LEN_STRING(str);
cstr = CSTR_STRING(str) + lstr;
memcpy( cstr, buf, buflen+1 );
SET_LEN_STRING(str, lstr+buflen); if ( buf[buflen-1] == '\n' ) break;
}
// fix the length of <str>
len = GET_LEN_STRING(str);
ResizeBag( str, SIZEBAG_STRINGLEN(len) );
// and return return len == 0 ? Fail : str;
}
/**************************************************************************** ** *F FuncREAD_ALL_FILE( <self>, <fid>, <limit> ) . . . . . . . read remainder ** ** more precisely, read until either ** (a) we have read at least one byte and no more are available ** (b) we have evidence that it will never be possible to read a byte ** (c) we have read <limit> bytes (-1 indicates no limit)
*/
static Obj FuncREAD_ALL_FILE(Obj self, Obj fid, Obj limit)
{ Char buf[20000]; Int len; // Length of string read this loop (or negative for error)
UInt lstr;
Obj str;
UInt csize;
Int ifid = GetSmallInt(SELF_NAME, fid); Int ilim = GetSmallInt(SELF_NAME, limit);
/* read <fid> until we see eof or we've read at least
one byte and more are not immediately available */
str = NEW_STRING(0);
len = 0;
lstr = 0;
#ifdef SYS_IS_CYGWIN32
getmore: #endif while (ilim == -1 || len < ilim ) {
lstr = 0; if ( len > 0 && !HasAvailableBytes(ifid)) break; if (SyBufIsTTY(ifid)) { if (ilim == -1) {
Pr("#W Warning -- reading to end of input tty will never " "end\n",
0, 0);
csize = 20000;
} else
csize = ((ilim - len) > 20000) ? 20000 : ilim - len;
// fix the length of <str>
len = GET_LEN_STRING(str); #ifdef SYS_IS_CYGWIN32 // line end hackery
UInt i = 0, j = 0; while (i < len) { if (CHARS_STRING(str)[i] == '\r') { if (i < len - 1 && CHARS_STRING(str)[i + 1] == '\n') {
i++; continue;
} else
CHARS_STRING(str)[i] = '\n';
}
CHARS_STRING(str)[j++] = CHARS_STRING(str)[i++];
}
len = j;
SET_LEN_STRING(str, len); // If we have not yet read enough, and have read at least one character this loop, then read more if (ilim != -1 && len < ilim && lstr > 0) goto getmore; #endif
ResizeBag( str, SIZEBAG_STRINGLEN(len) );
// and return return len == 0 ? Fail : str;
}
/**************************************************************************** ** *F FuncSEEK_POSITION_FILE( <self>, <fid>, <pos> ) . seek position of stream
*/ static Obj FuncSEEK_POSITION_FILE(Obj self, Obj fid, Obj pos)
{ Int ret;
Int ifid = GetSmallInt(SELF_NAME, fid); Int ipos = GetSmallInt(SELF_NAME, pos);
ret = SyFseek( ifid, ipos ); return ret == -1 ? Fail : True;
}
/**************************************************************************** ** *F FuncWRITE_BYTE_FILE( <self>, <fid>, <byte> ) . . . . . . . write a byte
*/ static Obj FuncWRITE_BYTE_FILE(Obj self, Obj fid, Obj ch)
{ Int ifid = GetSmallInt(SELF_NAME, fid); Int ich = GetSmallInt(SELF_NAME, ch);
// call the system dependent function Int ret = SyEchoch( ich, ifid ); return ret == -1 ? Fail : True;
}
/**************************************************************************** ** *F FuncWRITE_STRING_FILE_NC( <self>, <fid>, <string> ) .write a whole string
*/ static Obj FuncWRITE_STRING_FILE_NC(Obj self, Obj fid, Obj str)
{ Int len = 0, l, ret; constchar *ptr;
// don't check the argument
RequireStringRep(SELF_NAME, str);
len = GET_LEN_STRING(str);
ptr = CONST_CSTR_STRING(str); while (len > 0) {
l = (len > 1048576) ? 1048576 : len;
ret = SyWrite(INT_INTOBJ(fid), ptr, l); if (ret == -1) {
SySetErrorNo(); return Fail;
}
len -= ret;
ptr += ret;
} returnTrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.