// 'PrintPromptHook' is a GAP-level variable which can be set to a function // for printing the GAP prompt. If not bound, 'STATE(Prompt)' is printed. static Obj PrintPromptHook = 0;
// A pointer to the current input file
TypInputFile * Input;
// A pointer to the current output file. It points to the top of the // stack 'OutputFiles'.
TypOutputFile * Output;
//
TypOutputFile * IgnoreStdoutErrout;
// The file identifier of the current input logfile. If it is not 0 the // scanner echoes all input from the files '*stdin*' and '*errin*' to // this file.
TypOutputFile * InputLog;
// The file identifier of the current output logfile. If it is not 0 the // scanner echoes all output to the files '*stdout*' and '*errout*' to // this file.
TypOutputFile * OutputLog;
// for debugging from GDB / lldb, we mark this as extern inline externinlinestruct IOModuleState * IO(void)
{ return (struct IOModuleState *)StateSlotsAtOffset(IOStateOffset);
}
// handle line continuation, i.e., backslash followed by new line; and // also the case when we run out of buffered data while (*input->ptr == '\\' || *input->ptr == 0) {
// if we run out of data, get more, and try again if (*input->ptr == 0) {
GetLine(input); continue;
}
// we have seen a backslash; so check now if it starts a // line continuation, i.e., whether it is followed by a line terminator if (input->ptr[1] == '\n') { // LF is the line terminator used in Unix and its relatives
input->ptr += 2;
} elseif (input->ptr[1] == '\r' && input->ptr[2] == '\n') { // CR+LF is the line terminator used by Windows
input->ptr += 3;
} else { // if we see a backlash without a line terminator after it, stop break;
}
// if we get here, we saw a line continuation; change the prompt to a // partial prompt from now on
SetPrompt("> ");
}
return *input->ptr;
}
// GET_NEXT_CHAR_NO_LC is like GetNextChar, but does not handle // line continuations. This is used when skipping to the end of the // current line, when handling comment lines. Char GET_NEXT_CHAR_NO_LC(TypInputFile * input)
{ char c = *(++input->ptr); return c ? c : GetLine(input);
}
Char PEEK_NEXT_CHAR(TypInputFile * input)
{ // store the current character char c = *input->ptr;
// read next character; this will increment input->ptr and then // possibly read in new line data, and so even might end up resetting // input->ptr to point at the start of the line buffer, which is // equal to Input->line+1 char next = GetNextChar(input);
// push back the previous character: first, return input->ptr to the // previous position; then, if we detect that GetNextChar read a new // line, also restore the previous character by placing it in the "push // back buffer"
GAP_ASSERT(input->ptr > input->line);
input->ptr--; if (input->ptr == input->line)
*input->ptr = c;
// return the next character return next;
}
Char PEEK_CURR_CHAR(TypInputFile * input)
{ Char c = *input->ptr;
// if no character is available then get one if (c == '\0') {
GAP_ASSERT(input->ptr > input->line);
input->ptr--;
c = GetNextChar(input);
}
return c;
}
void SKIP_TO_END_OF_LINE(TypInputFile * input)
{ Char c = *input->ptr; while (c != '\n' && c != '\r' && c != '\377')
c = GET_NEXT_CHAR_NO_LC(input);
}
constChar * GetInputLineBuffer(TypInputFile * input)
{
GAP_ASSERT(input); // first byte of Input->line is reserved for the pushback buffer, so add 1 return input->line + 1;
}
#ifdef HPCGAP /* Handle *defin*; redirect *errin* to *defin* if the default
* channel is already open. */ if (streq(filename, "*defin*") ||
(streq(filename, "*errin*") && IO()->DefaultInputStream)) return OpenDefaultInput(input); #endif
// try to open the input file
file = SyFopen(filename, "r", TRUE); if ( file == -1 ) return0;
// enter the file identifier and the file name #ifdef GAP_KERNEL_DEBUG // paranoia: fill with garbage, to verify we initialize everything
memset(input, 0x47, sizeof(TypInputFile)); #endif
input->prev = IO()->Input;
input->stream = 0;
input->file = file;
// enable echo for stdin and errin if (streq("*errin*", filename) || streq("*stdin*", filename))
input->echo = TRUE; else
input->echo = FALSE;
// revert to previous input
IO()->Input = input->prev;
if (input->stream) { // if the input stream supports seeking, update its position to // reflect the actual state of things: we may have read and buffered // more bytes than we actually processed int offset = strlen(input->ptr); // check for EOF if (input->ptr[0] == '\377' && input->ptr[1] == '\0')
offset = 0; if (offset) {
Obj pos = CALL_1ARGS(PositionStream, input->stream);
C_DIFF_FIA(pos, pos, INTOBJ_INT(offset));
CALL_2ARGS(SeekPositionStream, input->stream, pos);
}
} else { // close the input file
SyFclose(input->file);
}
// refuse to open a logfile if we already log to one if (IO()->InputLog != 0 || IO()->OutputLog != 0) return0;
// try to open the file
IO()->OutputLogFileOrStream.file = SyFopen(filename, "w", FALSE);
IO()->OutputLogFileOrStream.stream = 0; if (IO()->OutputLogFileOrStream.file == -1) return0;
// refuse to open a logfile if we already log to one if (IO()->InputLog != 0) return0;
// try to open the file
IO()->InputLogFileOrStream.file = SyFopen(filename, "w", FALSE);
IO()->InputLogFileOrStream.stream = 0; if (IO()->InputLogFileOrStream.file == -1) return0;
// refuse to open a logfile if we already log to one if (IO()->OutputLog != 0) return0;
// try to open the file
memset(&IO()->OutputLogFileOrStream, 0, sizeof(TypOutputFile));
IO()->OutputLogFileOrStream.stream = stream;
IO()->OutputLogFileOrStream.file = -1;
IO()->OutputLog = &IO()->OutputLogFileOrStream;
// otherwise indicate success return1;
}
/**************************************************************************** ** *FCloseOutputLog()...............closethecurrentlogfile ** **'CloseOutputLog'closesthecurrentlogfileagain,sothatoutputto **'*stdout*'and'*errout*'willnolongerbeechoedtoafile. **'CloseOutputLog'willreturn1toindicatesuccess. ** **'CloseOutputLog'willfailifthereisnologfileactiveandwillreturn **0inthiscase.
*/
UInt CloseOutputLog ( void )
{ // refuse to close a non existent logfile if (IO()->OutputLog == 0) return0;
// refuse to close a log opened with LogTo if (IO()->OutputLog == IO()->InputLog) return0;
// close the logfile if (!IO()->OutputLog->stream) {
SyFclose(IO()->OutputLog->file);
}
// put the file on the stack, start at position 0 on an empty line #ifdef GAP_KERNEL_DEBUG // paranoia: fill with garbage, to verify we initialize everything
memset(output, 0x47, sizeof(TypOutputFile)); #endif
output->prev = IO()->Output;
IO()->Output = output;
output->isstringstream = (CALL_1ARGS(IsOutputStringStream, stream) == True);
output->stream = stream;
output->file = -1;
output->line[0] = '\0';
output->pos = 0;
output->format = (CALL_1ARGS(PrintFormattingStatus, stream) == True);
output->indent = 0;
// variables related to line splitting, very bad place to split
output->hints[0] = -1;
// silently refuse to close the test output file; this is probably an // attempt to close *errout* which is silently not opened, so let's // silently not close it if (IO()->IgnoreStdoutErrout == IO()->Output) return1;
GAP_ASSERT(output == IO()->Output);
// refuse to close the initial output file '*stdout*' #ifdef HPCGAP if (output->prev == 0 && output->stream &&
IO()->DefaultOutputStream == output->stream) return0; #else if (output->prev == 0) return0; #endif
// flush output and close the file
Pr("%c", (Int)'\03', 0); if (!output->stream) {
SyFclose(output->file);
}
// revert to previous output file and indicate success
IO()->Output = output->prev;
// don't keep GAP objects alive unnecessarily
output->stream = 0;
if ( input->stream ) { if (input->sline == 0 ||
(IS_STRING_REP(input->sline) &&
GET_LEN_STRING(input->sline) <= input->spos)) { // If we are in the process of quitting, we cannot call // GAP functions, so we just return EOF. if (STATE(UserHasQuit) || STATE(UserHasQUIT)) {
input->sline = Fail;
} else {
input->sline = CALL_1ARGS(ReadLineFunc, input->stream);
}
input->spos = 0;
} if ( input->sline == Fail || ! IS_STRING(input->sline) ) { return0;
}
ConvString(input->sline); /* we now allow that input->sline actually contains several lines,
e.g., it can be a string from a string stream */
// start position in buffer Char *bptr = buffer; while (*bptr)
bptr++;
// copy piece of input->sline into buffer and adjust counters constChar *ptr = CONST_CSTR_STRING(input->sline) + input->spos; constChar * const end = CONST_CSTR_STRING(input->sline) + GET_LEN_STRING(input->sline); constChar * const bend = buffer + length - 2; while (bptr < bend && ptr < end) { Char c = *ptr++;
// ignore CR, so that a Window CR+LF line terminator looks // to us the same as a Unix LF line terminator if (c == '\r') continue;
*bptr++ = c;
// check for line end if (c == '\n') break;
}
*bptr = '\0';
input->spos = ptr - CONST_CSTR_STRING(input->sline);
/* if input->stream is a string stream, we have to adjust the
position counter in the stream object as well */ if (input->isstringstream) {
ADDR_OBJ(input->stream)[1] = INTOBJ_INT(input->spos);
}
} else { if ( ! SyFgets( buffer, length, input->file ) ) { return0;
}
} return1;
}
// if file is '*stdin*' or '*errin*' print the prompt and flush it // if the GAP function `PrintPromptHook' is defined then it is called // for printing the prompt if (!input->stream) { if (input->file == 0 && SyQuiet) {
Pr("%c", (Int)'\03', 0);
} elseif (input->file == 0 || input->file == 2) { if (IO()->Output->pos > 0)
Pr("\n", 0, 0); if ( PrintPromptHook )
Call0ArgsInNewReader( PrintPromptHook ); else
Pr( "%s%c", (Int)STATE(Prompt), (Int)'\03' );
}
}
// bump the line number if (input->ptr > input->line && input->ptr[-1] == '\n') {
input->number++;
}
// initialize 'input->ptr', no errors on this line so far
input->line[0] = '\0'; // init the pushback buffer
input->line[1] = '\0'; // empty line buffer
input->ptr = input->line + 1;
input->lastErrorLine = 0;
// try to read a line if (!GetLine2(input)) {
input->ptr[0] = '\377';
input->ptr[1] = '\0';
}
// if necessary echo the line to the logfile if (IO()->InputLog != 0 && input->echo == 1) if (!(input->ptr[0] == '\377' && input->ptr[1] == '\0'))
PutLine2(IO()->InputLog, input->ptr, strlen(input->ptr));
// return the current character return *input->ptr;
}
// if necessary echo it to the logfile if (IO()->OutputLog != 0 && !stream->stream) { if (stream->file == 1 || stream->file == 3) {
PutLine2(IO()->OutputLog, stream->line, len);
}
}
}
/* helper function to add a hint about a possible line break;
a triple (pos, value, indent), such that the minimal (value-pos) wins */ staticvoid
addLineBreakHint(TypOutputFile * stream, Int pos, Int val, Int indentdiff)
{ Int nr, i; // find next free slot for (nr = 0; nr < MAXHINTS && stream->hints[3*nr] != -1; nr++); if (nr == MAXHINTS) { // forget the first stored hint for (i = 0; i < 3*MAXHINTS - 3; i++)
stream->hints[i] = stream->hints[i+3];
nr--;
} /* if pos is same as before only relevant if new entry has higher
priority */ if ( nr > 0 && stream->hints[3*(nr-1)] == pos )
nr--;
if ( stream->indent < pos &&
(stream->hints[3*nr] == -1 || val < stream->hints[3*(nr)+1]) ) {
stream->hints[3*nr] = pos;
stream->hints[3*nr+1] = val;
stream->hints[3*nr+2] = stream->indent;
stream->hints[3*nr+3] = -1;
}
stream->indent += indentdiff;
} /* helper function to find line break position,
returns position nr in stream[hints] or -1 if none found */ staticInt nrLineBreak(TypOutputFile * stream)
{ Int nr=-1, min, i; for (i = 0, min = INT_MAX; stream->hints[3*i] != -1; i++)
{ if (stream->hints[3*i] > 0 &&
stream->hints[3*i+1] - stream->hints[3*i] <= min)
{
nr = i;
min = stream->hints[3*i+1] - stream->hints[3*i];
}
} if (min < INT_MAX) return nr; else return -1;
}
staticvoid PutChrTo(TypOutputFile * stream, Char ch)
{ Int i, hint, spos; Char str [MAXLENOUTPUTLINE];
// if this is a better place to split the line remember it
addLineBreakHint(stream, stream->pos, 16*stream->indent, -1);
}
// '\03', print line elseif ( ch == '\03' ) {
// print the line if (stream->pos != 0)
{
stream->line[ stream->pos ] = '\0';
PutLineTo(stream, stream->pos );
// start the next line
stream->pos = 0;
} // reset line break hints
stream->hints[0] = -1;
}
// <newline> or <return>, print line, indent next elseif ( ch == '\n' || ch == '\r' ) {
// put the character on the line and terminate it
stream->line[ stream->pos++ ] = ch;
stream->line[ stream->pos ] = '\0';
// print the line
PutLineTo( stream, stream->pos );
// and dump it from the buffer
stream->pos = 0; if (stream->format)
{ // indent for next line for ( i = 0; i < stream->indent; i++ )
stream->line[ stream->pos++ ] = ' ';
} // reset line break hints
stream->hints[0] = -1;
}
// normal character, room on the current line #ifdef HPCGAP /* TODO: For threads other than the main thread, reserve some extra
space for the thread id indicator. See issue #136. */ elseif (stream->pos <
SyNrCols - 2 - 6 * (TLS(threadID) != 0) - IO()->NoSplitLine) { #else elseif (stream->pos < SyNrCols - 2 - IO()->NoSplitLine) { #endif
// put the character on this line
stream->line[ stream->pos++ ] = ch;
}
else
{ // position to split if ( (hint = nrLineBreak(stream)) != -1 )
spos = stream->hints[3*hint]; else
spos = 0;
/* if we are going to split at the end of the line, and we are
formatting discard blanks */ if ( stream->format && spos == stream->pos && ch == ' ' ) {
;
}
// full line, acceptable split position elseif ( stream->format && spos != 0 ) {
// add character to the line, terminate it
stream->line[ stream->pos++ ] = ch;
stream->line[ stream->pos++ ] = '\0';
// copy the rest after the best split position to a safe place for ( i = spos; i < stream->pos; i++ )
str[ i-spos ] = stream->line[ i ];
str[ i-spos] = '\0';
// print line up to the best split position
stream->line[ spos++ ] = '\n';
stream->line[ spos ] = '\0';
PutLineTo( stream, spos );
spos--;
// indent for the rest
stream->pos = 0; for ( i = 0; i < stream->hints[3*hint+2]; i++ )
stream->line[ stream->pos++ ] = ' ';
spos -= stream->hints[3*hint+2];
// copy the rest onto the next line for ( i = 0; str[ i ] != '\0'; i++ )
stream->line[ stream->pos++ ] = str[ i ]; // recover line break hints for copied rest for ( i = hint+1; stream->hints[3*i] != -1; i++ )
{
stream->hints[3*(i-hint-1)] = stream->hints[3*i]-spos;
stream->hints[3*(i-hint-1)+1] = stream->hints[3*i+1];
stream->hints[3*(i-hint-1)+2] = stream->hints[3*i+2];
}
stream->hints[3*(i-hint-1)] = -1;
}
// full line, no split position else {
if (stream->format)
{ /* append a '\',*/
stream->line[ stream->pos++ ] = '\\';
stream->line[ stream->pos++ ] = '\n';
} // and print the line
stream->line[ stream->pos ] = '\0';
PutLineTo( stream, stream->pos );
// add the character to the next line
stream->pos = 0;
stream->line[ stream->pos++ ] = ch;
/**************************************************************************** ** *FFuncPRINT_CPROMPT(<prompt>) ** **printscurrent`Prompt'ifargument<prompt>isnotinStringRep,otherwise **usesthecontentof<prompt>as`Prompt'(atmost80characters). **(importantistheflushcharacterwithoutresettingthecursorcolumn)
*/ static Obj FuncPRINT_CPROMPT(Obj self, Obj prompt)
{ if (IS_STRING_REP(prompt)) { /* by assigning to Prompt we also tell readline (if used) what the
current prompt is */
SetPrompt(CONST_CSTR_STRING(prompt));
}
Pr("%s%c", (Int)STATE(Prompt), (Int)'\03' ); return (Obj) 0;
}
// loop over the characters of the <format> string for ( p = format; *p != '\0'; p++ ) {
// not a '%' character, simply print it if ( *p != '%' ) {
put_a_char(state, *p); continue;
}
// if the character is '%' do something special
// first look for a precision field
p++;
prec = 0;
fill = (*p == '0' ? '0' : ' '); while ( IsDigit(*p) ) {
prec = 10 * prec + *p - '0';
p++;
}
// handle the case of a missing argument if (arg1 == 0 && (*p == 's' || *p == 'S' || *p == 'C' || *p == 'I')) {
put_a_char(state, '<');
put_a_char(state, 'n');
put_a_char(state, 'u');
put_a_char(state, 'l');
put_a_char(state, 'l');
put_a_char(state, '>');
// on to the next argument
arg1 = arg2;
}
// '%d' print an integer elseif (*p == 'd') { int is_neg = (arg1 < 0); if ( is_neg ) {
arg1 = -arg1;
prec--; // we loose one digit of output precision for the minus sign
}
// compute how many characters this number requires for ( n = 1; n <= arg1/10; n*=10 ) {
prec--;
} while ( --prec > 0 ) put_a_char(state, fill);
if ( is_neg ) {
put_a_char(state, '-');
}
for ( ; n > 0; n /= 10 )
put_a_char(state, (Char)(((arg1/n)%10) + '0') );
// on to the next argument
arg1 = arg2;
}
// '%s' or '%g' print a string elseif ( *p == 's' || *p == 'g') {
UInt len;
// If arg is a GAP obj, get out the contained string, and // set arg1obj so we can re-evaluate after any possible GC // which occurs in put_a_char if (*p == 'g') {
arg1obj = (Obj)arg1;
arg1 = (Int)CONST_CSTR_STRING(arg1obj);
len = GET_LEN_STRING(arg1obj);
} else {
arg1obj = 0;
len = strlen((constChar *)arg1);
}
// compute how many characters this identifier requires if (len >= prec)
prec = 0; else
prec -= len;
// if wanted push an appropriate number of <space>-s while ( prec-- > 0 ) put_a_char(state, ' ');
if (arg1obj) {
arg1 = (Int)CONST_CSTR_STRING(arg1obj);
}
// print the string /* must be careful that line breaks don't go inside
escaped sequences \n or \123 or similar */ for (UInt i = 0; i < len; i++) { constChar* q = ((constChar *)arg1) + i; // skip null bytes; this means 'prec' is off in this case. Fixing this // is not worth the effort. So instead, new rule: if you print strings // with null bytes, behavior of 'prec' is undefined. if (*q == '\0') continue; if (*q == '\\' && IO()->NoSplitLine == 0) { if (*(q + 1) < '8' && *(q + 1) >= '0')
IO()->NoSplitLine = 3; else
IO()->NoSplitLine = 1;
} elseif (IO()->NoSplitLine > 0)
IO()->NoSplitLine--;
put_a_char(state, *q);
if (arg1obj) {
arg1 = (Int)CONST_CSTR_STRING(arg1obj);
}
}
// on to the next argument
arg1 = arg2;
}
// '%C' print a string with the necessary C escapes elseif ( *p == 'C' ) {
// else raise an error else { for ( p = "%format error"; *p != '\0'; p++ )
put_a_char(state, *p);
}
}
}
staticvoid putToTheStream(void *state, Char c)
{
PutChrTo((TypOutputFile *)state, c);
}
staticvoid
PrTo(TypOutputFile * stream, constChar * format, Int arg1, Int arg2)
{
FormatOutput( putToTheStream, stream, format, arg1, arg2);
}
void Pr ( constChar * format, Int arg1, Int arg2 )
{ #ifdef HPCGAP if (!IO()->Output) {
OpenDefaultOutput(&IO()->DefaultOutput);
} #endif
PrTo(IO()->Output, format, arg1, arg2);
}
#else // register global bags with the garbage collector
InitGlobalBag(&(IO()->InputLogFileOrStream.stream), "src/io.c:InputLogFileOrStream");
InitGlobalBag(&(IO()->OutputLogFileOrStream.stream), "src/io.c:OutputLogFileOrStream"); #endif
/**************************************************************************** ** *FInitInfoIO()................tableofinitfunctions
*/ static StructInitInfo module = { // init struct using C99 designated initializers; for a full list of // fields, please refer to the definition of StructInitInfo
.type = MODULE_BUILTIN,
.name = "io",
.initKernel = InitKernel,
.initLibrary = InitLibrary,
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.112Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-13)
¤
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 und die Messung sind noch experimentell.