| 1 | Implementation of stdio.
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 | /* Array of strings: result of printing to stdout */
|
|---|
| 5 | $output char CIVL_stdout[][];
|
|---|
| 6 |
|
|---|
| 7 | /* Abstract functions will be declared in LibstdioExecutor.java:
|
|---|
| 8 | * char[] intToString(int x, char format[])
|
|---|
| 9 | * char[] floatToString(float x, char format[])
|
|---|
| 10 | * char[] doubleToString(double x, char format[])
|
|---|
| 11 | * etc.
|
|---|
| 12 | */
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 | /* system function printf will be defined as follows:
|
|---|
| 16 | * evaluate arguments.
|
|---|
| 17 | * parse the format string to find all the % codes.
|
|---|
| 18 | * check they match up with the number and types of additional args.
|
|---|
| 19 | * for each code, extract the code into a separate concrete array
|
|---|
| 20 | * of char.
|
|---|
| 21 | * For an abstract function call for each of the above format-string-
|
|---|
| 22 | * value pairs. Note that %s codes do not require abstract function
|
|---|
| 23 | * since corresponding value will already have type string (char[]).
|
|---|
| 24 | * Construct an array of SymbolicExpression as follows:
|
|---|
| 25 | * sequence of concrete strings interleaved with abstract ones
|
|---|
| 26 | * as above.
|
|---|
| 27 | * Append these to CIVL_stdout.
|
|---|
| 28 | */
|
|---|
| 29 |
|
|---|
| 30 |
|
|---|
| 31 | Input:
|
|---|
| 32 |
|
|---|
| 33 | strcmp(s1,s2): to determine if two strings are equal.
|
|---|
| 34 | Need to assume this is true in many cases?
|
|---|
| 35 | Need strcmp to return abstract function if the strings
|
|---|
| 36 | are not concrete.
|
|---|
| 37 |
|
|---|
| 38 | fscanf(file, format, buf):
|
|---|
| 39 |
|
|---|
| 40 | $input
|
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 | =================================== I/O Model =========================================
|
|---|
| 45 |
|
|---|
| 46 | Representation of file contents:
|
|---|
| 47 |
|
|---|
| 48 | A file is either a text file or a binary file.
|
|---|
| 49 |
|
|---|
| 50 | Text files: the contents of a text file is a sequence of strings, i.e., a ragged array
|
|---|
| 51 | of char.
|
|---|
| 52 |
|
|---|
| 53 | char[][] tf; /* ragged */
|
|---|
| 54 |
|
|---|
| 55 | Each element tf[i] is a "word".
|
|---|
| 56 | A word is a string, i.e., an array of char.
|
|---|
| 57 |
|
|---|
| 58 | Furthermore, a word is either a white-space word (ws) or a
|
|---|
| 59 | non-white-space word (x). A white-space word consists of only
|
|---|
| 60 | white-space characters (newline, space, tab, return).
|
|---|
| 61 | A non-white-space word consists of only
|
|---|
| 62 | non-white-space characters. The elements of tf alternate between
|
|---|
| 63 | white-space words and non-white-space words
|
|---|
| 64 |
|
|---|
| 65 | tf may start with either a white-space word (ws) or a non-white-space
|
|---|
| 66 | word (x). It may end with either a ws or an x. Or, it may be empty.
|
|---|
| 67 | Examples:
|
|---|
| 68 |
|
|---|
| 69 | tf = {ws, x, ws, x, ..., ws, x, ws}
|
|---|
| 70 | tf = {x, ws, x, ..., ws, x, ws}
|
|---|
| 71 | tf = {ws, x, ws, x, ..., ws, x}
|
|---|
| 72 | tf = {x, ws, x, ..., ws, x}
|
|---|
| 73 | tf = {x}
|
|---|
| 74 | tf = {ws}
|
|---|
| 75 | tf = {}
|
|---|
| 76 |
|
|---|
| 77 | The following abstract functions may be used to create words:
|
|---|
| 78 |
|
|---|
| 79 | (However, I'm not sure how to represent an abstract function
|
|---|
| 80 | that returns a String, which is an array, since a function
|
|---|
| 81 | cannot return an array in C or CIVL-C. However you can do this
|
|---|
| 82 | in the model layer.)
|
|---|
| 83 |
|
|---|
| 84 | String intToString(int n, String format);
|
|---|
| 85 | String realToString(real x, String format);
|
|---|
| 86 |
|
|---|
| 87 | A position in the file is represented by an ordered pair (int, int).
|
|---|
| 88 | It is an index into the ragged array.
|
|---|
| 89 |
|
|---|
| 90 | Scanning: scanning reads the array and converts to either string, int, or real.
|
|---|
| 91 | The following abstract functions are used to convert:
|
|---|
| 92 |
|
|---|
| 93 | int stringToInt(String s, String format);
|
|---|
| 94 | real stringToRead(String s, String format);
|
|---|
| 95 |
|
|---|
| 96 | These satisfy:
|
|---|
| 97 |
|
|---|
| 98 | stringToInt(intToString(n,f),f) = n
|
|---|
| 99 | intToString(stringToInt(s,f),f) = s
|
|---|
| 100 |
|
|---|
| 101 | stringToReal(realToString(x,f),f) not necessarily equal to x (approx.)
|
|---|
| 102 | realToString(stringToReal(s,f),f) depends on precision of real type
|
|---|
| 103 |
|
|---|
| 104 | Note strings do not require any conversion, except that a null termination
|
|---|
| 105 | character is added when scanned. (Is it included when printing?)
|
|---|
| 106 |
|
|---|
| 107 | Printing adds elements to the array.
|
|---|
| 108 |
|
|---|
| 109 | Binary files: work in progress.
|
|---|
| 110 |
|
|---|
| 111 | State: each process has a process-global variable filesystem, which is a vector
|
|---|
| 112 | of files.
|
|---|
| 113 |
|
|---|
| 114 | File System:
|
|---|
| 115 |
|
|---|
| 116 | A file has a name, as well as contents. The name is a string.
|
|---|
| 117 | A process global variable in each process is used to represent the file
|
|---|
| 118 | system on that process. This is a vector of files.
|
|---|
| 119 |
|
|---|
| 120 | The function to open a file specifies the name. There are two possibilities:
|
|---|
| 121 | a file of that name already exists in the filesystem variable, or it doesn't.
|
|---|
| 122 | This may not be determinable. For example, a program that takes two commandline
|
|---|
| 123 | inputs x and y could then proceed to open x, and then open y. What results from
|
|---|
| 124 | this depends on whether x equals y (as strings).
|
|---|
| 125 |
|
|---|
| 126 | Question: is it legal to open a file twice, i.e, have two streams associated
|
|---|
| 127 | to the same file? (Why not?)
|
|---|
| 128 |
|
|---|
| 129 | FOR NOW: if we cannot determine whether two filenames are equal or not:
|
|---|
| 130 | assume they are not, and print a warning that we are adding this assumption.
|
|---|
| 131 |
|
|---|
| 132 | LATER: Force user to add assumption that x does not equal y? This requires
|
|---|
| 133 | string operations (equals, strcmp).
|
|---|
| 134 |
|
|---|
| 135 | #pragma TASS assume argc >= 3 && strcmp(argv[1],argv[2]) != 0
|
|---|
| 136 |
|
|---|
| 137 | User might even put this in his code as an assertion. (Should we have a way
|
|---|
| 138 | to indicate an assertion should be assumed to hold?
|
|---|
| 139 |
|
|---|
| 140 | #pragma TASS assume holds
|
|---|
| 141 | assert argc >= 3 && strcmp(argv[1],argv[2]) != 0
|
|---|
| 142 |
|
|---|
| 143 | In any case, if a file of that name does not exist in file system variable, it
|
|---|
| 144 | is added. Otherwise, the exisitng one is used.
|
|---|
| 145 |
|
|---|
| 146 | If the open command specifies write mode, the file contents are put
|
|---|
| 147 | in an initial empty state.
|
|---|
| 148 |
|
|---|
| 149 | If the open command specifies read mode and the file has just been added, the file
|
|---|
| 150 | contents are initialized with a symbolic constant of the approrpriate type.
|
|---|
| 151 | The length of the contents array is a symbolic constant of integer type, the ragged
|
|---|
| 152 | array is constructed as usual for ragged arrays.
|
|---|
| 153 |
|
|---|
| 154 | If the open command specifies read mode and the file alread existed, the file
|
|---|
| 155 | contents are left unchanged.
|
|---|
| 156 |
|
|---|
| 157 | Closing: closing a file does not remove it from the file system variable.
|
|---|
| 158 | (Only deleting the file can do that.) It simply sets a flag in the stream
|
|---|
| 159 | associated with the file. If the file was written to since being opened,
|
|---|
| 160 | it also causes the file to be copied to the output variable which is an array
|
|---|
| 161 | of files. If the file is already there in the output variable, it is overwritten.
|
|---|
| 162 |
|
|---|
| 163 | Multi-process programs: for now, model will be as follows: each process has
|
|---|
| 164 | its own filesystem, so no communication is possible across procs.
|
|---|
| 165 |
|
|---|
| 166 | Single program verification mode: no output variable is used.
|
|---|
| 167 | Output to stdout/stderr: simply ignored.
|
|---|
| 168 | Input from stdin: only one process can do it, otherwise error.
|
|---|
| 169 |
|
|---|
| 170 | Comparison mode: a single output array of output files is used. At process
|
|---|
| 171 | termination, output files from a process will be copied to this array.
|
|---|
| 172 | They will be inserted in alphabetical (or some other canonical) order.
|
|---|
| 173 | It is OK to have output files from different procs, as long as they have
|
|---|
| 174 | different names. For example, this will allow comparison of two programs,
|
|---|
| 175 | one sequential
|
|---|
| 176 | and one parallel, where the sequential one writes file OUT and the 7th
|
|---|
| 177 | process (say) of the parallel one writes file OUT.
|
|---|
| 178 | Exception: output to stdout/stderr could be ignored (commandline option).
|
|---|
| 179 | If not ignored, only process can write to each (but they can be two
|
|---|
| 180 | different procs), i.e., they are treated like all other output files.
|
|---|
| 181 | -ignorestdout=BOOLEAN (default: false). -ignorestderr.
|
|---|
| 182 |
|
|---|
| 183 | Also, any output statement can be preceded by #pragma TASS ignore and it
|
|---|
| 184 | will be ignored.
|
|---|
| 185 |
|
|---|
| 186 |
|
|---|
| 187 | TODO: add IO error to types of execution errors.
|
|---|
| 188 |
|
|---|
| 189 | What about format errors from scanning? ignore for now---add assumptions later.
|
|---|
| 190 |
|
|---|
| 191 | Note: make sure all streams are closed at termination. Else throw I/O error.
|
|---|
| 192 |
|
|---|
| 193 |
|
|---|
| 194 | =================================== Types =============================================
|
|---|
| 195 |
|
|---|
| 196 |
|
|---|
| 197 | The following represents a file, as well as the state of the file.
|
|---|
| 198 |
|
|---|
| 199 | A file has a filename, which is a String.
|
|---|
| 200 | Note: the maximum length of a filename is FILENAME_MAX, a constant defined
|
|---|
| 201 | in C99 Standard.
|
|---|
| 202 |
|
|---|
| 203 | An output file is one which has been written to at least once in the
|
|---|
| 204 | execution of the program, unless the user has explicitly said to ignore
|
|---|
| 205 | such writes (using the TASS ignore pragma).
|
|---|
| 206 | An input file is one which has been read at least once before writing
|
|---|
| 207 | to it.
|
|---|
| 208 |
|
|---|
| 209 | Every file is either in binary or text form.
|
|---|
| 210 |
|
|---|
| 211 | Every file has either byte or wide orientation. This is a C thing.
|
|---|
| 212 | It has to do with whether a character is represented by one byte, or
|
|---|
| 213 | more than one byte. See the C99 Standard. (Is this just for text
|
|---|
| 214 | files?)
|
|---|
| 215 |
|
|---|
| 216 |
|
|---|
| 217 | typedef struct STDIO_File {
|
|---|
| 218 | char filename[]; /* the name of the file (including path) */
|
|---|
| 219 | boolean isOutput; /* is this an output file? */
|
|---|
| 220 | boolean isInput; /* is this an input file? */
|
|---|
| 221 | boolean isBinary; /* can be binary or text */
|
|---|
| 222 | boolean isWide; /* wide orientation */
|
|---|
| 223 | char[][] contents; /* file contents: ragged array */
|
|---|
| 224 | } STDIO_File;
|
|---|
| 225 |
|
|---|
| 226 |
|
|---|
| 227 | The following represents a stream, which is the data structure
|
|---|
| 228 | used by the user program to access a file. A stream has a reference
|
|---|
| 229 | to a file, and some additional information.
|
|---|
| 230 |
|
|---|
| 231 | The stream is either open or closed.
|
|---|
| 232 |
|
|---|
| 233 | The stream has a reference to a certain position in the file.
|
|---|
| 234 | This is represented as an integer.
|
|---|
| 235 |
|
|---|
| 236 | The stream has a certain mode, coded as "r" (read), "w" (write),
|
|---|
| 237 | and so on. Internally, integer constants are used to represent
|
|---|
| 238 | the modes.
|
|---|
| 239 |
|
|---|
| 240 | typedef struct STDIO_Stream {
|
|---|
| 241 | STDIO_File *file; /* file to which this stream refers */
|
|---|
| 242 | boolean isOpen; /* is this stream open? */
|
|---|
| 243 | int position1; /* current word in file */
|
|---|
| 244 | int position2; /* character index in word */
|
|---|
| 245 | int mode; /* Stream mode: r/w/a */
|
|---|
| 246 | } FILE;
|
|---|
| 247 |
|
|---|
| 248 | #define size_t int (integer)
|
|---|
| 249 |
|
|---|
| 250 | #define fpos_t int (integer)
|
|---|
| 251 |
|
|---|
| 252 | Notes: According to C99,
|
|---|
| 253 | there is also an "orientation" associated to a stream: either "byte"
|
|---|
| 254 | or "wide". The orientation of the stream is determined by the first I/O function
|
|---|
| 255 | that is applied to the stream after opening it. Here we are only dealing with
|
|---|
| 256 | byte streams. There are corresponding wide versions of all the functions
|
|---|
| 257 | for wide streams. We have put this characteristic in the file itself.
|
|---|
| 258 |
|
|---|
| 259 | =================================== Constants =========================================
|
|---|
| 260 |
|
|---|
| 261 | Note: these can be put in stdio.h
|
|---|
| 262 |
|
|---|
| 263 | These are for internal use only. They are used instead of the strings "r",
|
|---|
| 264 | "w", etc.:
|
|---|
| 265 |
|
|---|
| 266 | #define STDIO_MODE_READ 1
|
|---|
| 267 | #define STDIO_MODE_WRITE 2
|
|---|
| 268 | #define STDIO_MODE_APPEND 3
|
|---|
| 269 |
|
|---|
| 270 | #define stdin (FILE*)0
|
|---|
| 271 | #define stdout (FILE*)1
|
|---|
| 272 | #define stderr (FILE*)2
|
|---|
| 273 |
|
|---|
| 274 | #define EOF -1
|
|---|
| 275 |
|
|---|
| 276 |
|
|---|
| 277 |
|
|---|
| 278 | ===================================== Variables =======================================
|
|---|
| 279 |
|
|---|
| 280 | File objects (instances of STDIO_File) are instanitated at runtime.
|
|---|
| 281 | A file is instantiated when one is opened for reading or writing.
|
|---|
| 282 |
|
|---|
| 283 | Output variable:
|
|---|
| 284 | Vector<STDIO_File> STDIO_outputFiles;
|
|---|
| 285 |
|
|---|
| 286 | The files that had something written to them. When an output file is closed,
|
|---|
| 287 | it is copied from the heap to this vector. This vector could be sorted to
|
|---|
| 288 | get a canonical order.
|
|---|
| 289 |
|
|---|
| 290 | Process global variable:
|
|---|
| 291 | Vector<STDIO_File> STDIO_filesystem
|
|---|
| 292 |
|
|---|
| 293 | This represents the state of all files in the file system on this process.
|
|---|
| 294 | It is initially empty. When a file is opened (whether for reading or writing)
|
|---|
| 295 | a file object is instantiated (if one with that filename does not already exist)
|
|---|
| 296 | on the heap, and a reference to it is added to this vector.
|
|---|
| 297 | This vector could be sorted to get a canonical order.
|
|---|
| 298 |
|
|---|
| 299 |
|
|---|
| 300 | ================================== Abstract Functions =================================
|
|---|
| 301 |
|
|---|
| 302 |
|
|---|
| 303 |
|
|---|
| 304 | =================================== System Functions ==================================
|
|---|
| 305 |
|
|---|
| 306 |
|
|---|
| 307 |
|
|---|
| 308 | FILE *fopen(const char * restrict filename, const char * restrict mode);
|
|---|
| 309 | See 7.19.5.3.
|
|---|
| 310 | modes:
|
|---|
| 311 | r: open text file for reading
|
|---|
| 312 | w: truncate if already exists, open text file for writing
|
|---|
| 313 | a: open text file for writing at end (append)
|
|---|
| 314 | rb: open binary file for reading.
|
|---|
| 315 | wb:
|
|---|
| 316 | ab:
|
|---|
| 317 | all of above with "+" added.
|
|---|
| 318 | returns NULL if problem
|
|---|
| 319 | Examples:
|
|---|
| 320 | FILE *f = fopen(argv[0], "r");
|
|---|
| 321 |
|
|---|
| 322 | int fclose(FILE *stream);
|
|---|
| 323 | all data is flushed automatically at close.
|
|---|
| 324 | returns 0 if successful, EOF if problem.
|
|---|
| 325 |
|
|---|
| 326 | int fprintf(FILE * restrict stream, const char * restrict format, ...);
|
|---|
| 327 | some of the format codes:
|
|---|
| 328 | %d: integer
|
|---|
| 329 | %f: floating-point
|
|---|
| 330 | %s: string, don't print the null terminating character
|
|---|
| 331 | many different variations are possible
|
|---|
| 332 | returns the number of characters transmitted
|
|---|
| 333 | returns negative number if error
|
|---|
| 334 | Examples:
|
|---|
| 335 | fprintf(stdout, "blah %d blah %d %f \n", a[i], n, x);
|
|---|
| 336 | equivalent to:
|
|---|
| 337 | fprintf(stdout, "blah ");
|
|---|
| 338 | fprintf(stdout, "%d", a[i]);
|
|---|
| 339 | fprintf(stdout, " blah ");
|
|---|
| 340 | fprintf(stdout, "%d", n);
|
|---|
| 341 | fprintf(stdout, " ");
|
|---|
| 342 | fprintf(stdout, "%f", x);
|
|---|
| 343 | fprintf(stdout, " \n");
|
|---|
| 344 | translated as:
|
|---|
| 345 |
|
|---|
| 346 | int fscanf(FILE * restrict stream, const char * restrict format, ...);
|
|---|
| 347 |
|
|---|
| 348 | Notes:
|
|---|
| 349 | the extra arguments are pointers to things of the appropriate type. They point
|
|---|
| 350 | to where the result of the scans should go
|
|---|
| 351 | the format string is a sequence of directives. each directive is either
|
|---|
| 352 | an ordinary non-white-space character, a sequence of white-space characters,
|
|---|
| 353 | or a conversion specification
|
|---|
| 354 | each directive matches some sequence of characters in the file
|
|---|
| 355 | white space directive: reads until first non-white space character
|
|---|
| 356 | is encountered
|
|---|
| 357 | regular non-white-space characters have to be matched exactly,
|
|---|
| 358 | in which case they are scanned and ignored.
|
|---|
| 359 | initial white-space characters are scanned and ignored
|
|---|
| 360 | the longest character sequence matching the code is read--hence reading one
|
|---|
| 361 | more character will cause non-match (or be EOF)
|
|---|
| 362 | %s: matches sequence of non-white-space characters. The null tern character
|
|---|
| 363 | is automatically added to the result
|
|---|
| 364 | %d: matches sequence of chars which define an int
|
|---|
| 365 | %f: matches sequence of chars which define a floating-point number. Needs to have
|
|---|
| 366 | one of a few legal formats, such as 456.23 (note: need decimal point),
|
|---|
| 367 | or exponent....see strtod, 7.20.1.3
|
|---|
| 368 |
|
|---|
| 369 | Returns:
|
|---|
| 370 | number of items assigned. this can be less than number of conversion
|
|---|
| 371 | specifications due to matching errors
|
|---|
| 372 | EOF, if input error occurs: encoding error or unavailability of input characters
|
|---|
| 373 |
|
|---|
| 374 | Reading files.
|
|---|
| 375 |
|
|---|
| 376 | #pragma TASS system
|
|---|
| 377 | FILE* STDIO_openRead(char *filename, boolean isBinary, boolean isWide);
|
|---|
| 378 |
|
|---|
| 379 | #pragma TASS system
|
|---|
| 380 | FILE* STDIO_openWrite(char *filename, boolean isBinary, boolean isWide);
|
|---|
| 381 |
|
|---|
| 382 | /* Closing the file also causes output to be copied to output variable STDIO_outputFiles */
|
|---|
| 383 | #pragma TASS system
|
|---|
| 384 | void close(FILE *file);
|
|---|
| 385 |
|
|---|
| 386 | Use:
|
|---|
| 387 |
|
|---|
| 388 | int STDIO_scanInt(FILE *stream, int *x) {
|
|---|
| 389 | *x = STDIO_SCAN_INT(*(stream->file), stream->position);
|
|---|
| 390 | stream->position += sizeof(int);
|
|---|
| 391 | return 1; // number of items scanned successfully
|
|---|
| 392 | }
|
|---|
| 393 |
|
|---|
| 394 | int STDIO_scanReal(FILE *stream, float *x) {
|
|---|
| 395 | return 0;
|
|---|
| 396 |
|
|---|
| 397 | }
|
|---|
| 398 |
|
|---|
| 399 | int STDIO_scanString(FILE *stream, char *string) {
|
|---|
| 400 | return 0;
|
|---|
| 401 |
|
|---|
| 402 | }
|
|---|
| 403 |
|
|---|
| 404 | /* The following takes the file and current position and returns the
|
|---|
| 405 | * new value for contents after writing the int x to the file. */
|
|---|
| 406 | #pragma TASS abstract int STDIO_PRINT_INT(FILE file, int position, int x);
|
|---|
| 407 |
|
|---|
| 408 | void STDIO_printInt(FILE *stream, int x) {
|
|---|
| 409 | stream->file->contents = STDIO_PRINT_INT(stream->file, stream->position, x);
|
|---|
| 410 | stream->position += sizeof(int);
|
|---|
| 411 | }
|
|---|
| 412 |
|
|---|
| 413 |
|
|---|
| 414 | void STDIO_printReal(FILE *file, float x) {
|
|---|
| 415 | }
|
|---|
| 416 |
|
|---|
| 417 | void STDIO_printString(FILE *file, char *string) {
|
|---|
| 418 | }
|
|---|
| 419 |
|
|---|
| 420 |
|
|---|
| 421 | ----
|
|---|
| 422 |
|
|---|
| 423 | Transformations:
|
|---|
| 424 |
|
|---|
| 425 | AST:
|
|---|
| 426 |
|
|---|
| 427 | add type STDIO_File: requires ragged array type in AST classes
|
|---|
| 428 | replace definition of type FILE with new definition; all references to the old type
|
|---|
| 429 | now point to new type. Should not be necessary because the type itself will
|
|---|
| 430 | be changed.
|
|---|
| 431 | modify functions?
|
|---|
| 432 |
|
|---|
| 433 | Model:
|
|---|
| 434 |
|
|---|
| 435 | add abstract functions
|
|---|
| 436 | for any process that uses stdio functions:
|
|---|
| 437 | add process global variable STDIO_filesystem
|
|---|
| 438 | add output variable STDIO_outputFiles
|
|---|
| 439 |
|
|---|
| 440 | Executor:
|
|---|
| 441 | implement all functions as system functions
|
|---|
| 442 |
|
|---|
| 443 | initialization?
|
|---|
| 444 | each library should have opportunity to initialize state: check!
|
|---|
| 445 | each library should have function called at termination: check!
|
|---|
| 446 | create entries in file system for stdin, stdout, stderr.
|
|---|
| 447 | but these are constants must be created in initial states
|
|---|
| 448 | the last two are output files?
|
|---|
| 449 | add to STDIO_filesystem: stdin, stdout, stderr files
|
|---|
| 450 | open streams on them: process global variables?
|
|---|
| 451 | replace references to (FILE*)0 to pointers to those streams. could be done
|
|---|
| 452 | dynamically.
|
|---|
| 453 | the name "stdout" is different from a user file named "stdout". Do not
|
|---|
| 454 | confuse these two. add bit isSystem?
|
|---|
| 455 | for printf, etc., VARIABLE ARGUMENTs
|
|---|
| 456 | at termination, close the three system streams.
|
|---|
| 457 |
|
|---|
| 458 |
|
|---|