1 /*
2  * PortProcedures.cpp - <port> procedures.
3  *
4  *   Copyright (c) 2008  Higepon(Taro Minowa)  <higepon@users.sourceforge.jp>
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *   1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *
13  *   2. Redistributions in binary form must reproduce the above copyright
14  *      notice, this list of conditions and the following disclaimer in the
15  *      documentation and/or other materials provided with the distribution.
16  *
17  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
23  *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  $Id$
30  */
31 
32 // #ifdef _WIN32
33 //     #include <io.h>
34 // #else
35 // #include <unistd.h> // getcwd
36 // #endif
37 #include <sys/stat.h> // stat
38 #include <sys/types.h>
39 #include <fcntl.h>
40 #include "Object.h"
41 #include "Object-inl.h"
42 #include "Pair.h"
43 #include "Pair-inl.h"
44 #include "SString.h"
45 #include "Closure.h"
46 #include "ByteVector.h"
47 #include "PortProcedures.h"
48 #include "ProcedureMacro.h"
49 #include "TextualOutputPort.h"
50 #include "TextualInputPort.h"
51 #include "StringTextualOutputPort.h"
52 #include "Transcoder.h"
53 #include "Latin1Codec.h"
54 #include "UTF8Codec.h"
55 #include "UTF16Codec.h"
56 #include "StandardOutputPort.h"
57 #include "StandardErrorPort.h"
58 #include "StandardInputPort.h"
59 #include "ByteArrayBinaryInputPort.h"
60 #include "Symbol.h"
61 #include "EqHashTable.h"
62 #include "Bignum.h"
63 #include "SimpleStruct.h"
64 #include "FaslReader.h"
65 #include "FaslWriter.h"
66 #include "Arithmetic.h"
67 #include "ByteVector.h"
68 #include "OSCompat.h"
69 #include "CustomBinaryInputPort.h"
70 #include "CustomBinaryOutputPort.h"
71 #include "CustomTextualInputPort.h"
72 #include "CustomTextualOutputPort.h"
73 #include "CustomTextualInputOutputPort.h"
74 #include "BufferedFileBinaryInputPort.h"
75 #include "BufferedFileBinaryOutputPort.h"
76 #include "BinaryInputOutputPort.h"
77 #include "BufferedFileBinaryInputOutputPort.h"
78 #include "BlockBufferedFileBinaryOutputPort.h"
79 #include "FileBinaryInputOutputPort.h"
80 #include "BlockBufferedFileBinaryInputOutputPort.h"
81 #include "LineBufferedFileBinaryInputOutputPort.h"
82 #include "TranscodedTextualInputOutputPort.h"
83 #include "TranscodedTextualInputPort.h"
84 #include "TranscodedTextualOutputPort.h"
85 #include "BinaryInputOutputPort.h"
86 #include "ListProcedures.h"
87 #include "CustomBinaryInputOutputPort.h"
88 #include "ByteArrayBinaryOutputPort.h"
89 
90 using namespace scheme;
91 
utf8ToUtf32(const char * s,int len)92 ucs4string scheme::utf8ToUtf32(const char* s, int len)
93 {
94     ByteArrayBinaryInputPort in((uint8_t*)s, len);
95     UTF8Codec codec;
96     Transcoder transcoderr(&codec, EolStyle(LF), ErrorHandlingMode(IGNORE_ERROR));
97     return transcoderr.getString(&in);
98 }
99 
utf16ToUtf32(const char * s,int len)100 ucs4string scheme::utf16ToUtf32(const char* s, int len)
101 {
102     ByteArrayBinaryInputPort in((uint8_t*)s, len);
103     UTF16Codec codec;
104     Transcoder transcoderr(&codec, EolStyle(LF), ErrorHandlingMode(IGNORE_ERROR));
105     return transcoderr.getString(&in);
106 }
107 
108 // output is NULL terminated
utf32toUtf8(const ucs4string & s)109 char* scheme::utf32toUtf8(const ucs4string& s)
110 {
111     ByteArrayBinaryOutputPort out;
112     UTF8Codec codec;
113     Transcoder transcoderr(&codec, EolStyle(LF), ErrorHandlingMode(IGNORE_ERROR));
114     transcoderr.putString(&out, s);
115     if (!s.empty()) {
116         transcoderr.putChar(&out, '\0');
117     }
118     return (char*)out.toByteVector()->data();
119 }
120 
isExistOption(SimpleStruct * fileOptions,Object option)121 static bool isExistOption(SimpleStruct* fileOptions, Object option)
122 {
123     Object members = fileOptions->ref(1);
124     MOSH_ASSERT(members.isList());
125     return !memq(option, members).isFalse();
126 }
isNoFail(SimpleStruct * fileOptions)127 static bool isNoFail(SimpleStruct* fileOptions)
128 {
129     return isExistOption(fileOptions, Symbol::NO_FAIL);
130 }
131 
isNoCreate(SimpleStruct * fileOptions)132 static bool isNoCreate(SimpleStruct* fileOptions)
133 {
134     return isExistOption(fileOptions, Symbol::NO_CREATE);
135 }
136 
isNoTruncate(SimpleStruct * fileOptions)137 static bool isNoTruncate(SimpleStruct* fileOptions)
138 {
139     return isExistOption(fileOptions, Symbol::NO_TRUNCATE);
140 }
141 
isEmpty(SimpleStruct * fileOptions)142 static bool isEmpty(SimpleStruct* fileOptions)
143 {
144     return fileOptions->ref(1).isNil();
145 }
146 
makeCustomTextualInputOutputPortEx(VM * theVM,int argc,const Object * argv)147 Object scheme::makeCustomTextualInputOutputPortEx(VM* theVM, int argc, const Object* argv)
148 {
149     DeclareProcedureName("make-custom-textual-input/output-port");
150     checkArgumentLength(6);
151 
152     argumentAsString(0, id);
153     argumentCheckProcedure(1, readProc);
154     argumentCheckProcedure(2, writeDProc);
155     argumentCheckProcedureOrFalse(3, getPositionProc);
156     argumentCheckProcedureOrFalse(4, setPositionDProc);
157     argumentCheckProcedureOrFalse(5, closeProc);
158 
159     return Object::makeCustomTextualInputOutputPort(theVM, id->data(), readProc, writeDProc, getPositionProc, setPositionDProc, closeProc);
160 }
161 
makeCustomBinaryInputOutputPortEx(VM * theVM,int argc,const Object * argv)162 Object scheme::makeCustomBinaryInputOutputPortEx(VM* theVM, int argc, const Object* argv)
163 {
164     DeclareProcedureName("make-custom-binary-input/output-port");
165     checkArgumentLength(6);
166 
167     argumentAsString(0, id);
168     argumentCheckProcedure(1, readProc);
169     argumentCheckProcedure(2, writeProc);
170     argumentCheckProcedureOrFalse(3, getPositionProc);
171     argumentCheckProcedureOrFalse(4, setPositionProc);
172     argumentCheckProcedureOrFalse(5, closeProc);
173 
174     return Object::makeCustomBinaryInputOutputPort(theVM, id->data(), readProc, writeProc, getPositionProc, setPositionProc, closeProc);
175 }
176 
177 /*
178     file-options
179 
180     (file-options)
181       If file exists:     raise &file-already-exists
182       If does not exist:  create new file
183     (file-options no-create)
184       If file exists:     truncate
185       If does not exist:  raise &file-does-not-exist
186     (file-options no-fail)
187       If file exists:     truncate
188       If does not exist:  create new file
189     (file-options no-truncate)
190       If file exists:     raise &file-already-exists
191       If does not exist:  create new file
192     (file-options no-create no-fail)
193       If file exists:     truncate
194       If does not exist:  [N.B.] R6RS say nothing about this case, we choose raise &file-does-not-exist
195     (file-options no-fail no-truncate)
196       If file exists:     set port position to 0 (overwriting)
197       If does not exist:  create new file
198     (file-options no-create no-truncate)
199       If file exists:     set port position to 0 (overwriting)
200       If does not exist:  raise &file-does-not-exist
201     (file-options no-create no-fail no-truncate)
202       If file exists:     set port position to 0 (overwriting)
203       If does not exist:  [N.B.] R6RS say nothing about this case, we choose raise &file-does-not-exist
204 
205 */
openFileInputOutputPortEx(VM * theVM,int argc,const Object * argv)206 Object scheme::openFileInputOutputPortEx(VM* theVM, int argc, const Object* argv)
207 {
208     DeclareProcedureName("open-file-input/output-port");
209     checkArgumentLengthBetween(1, 4);
210     BinaryInputOutputPort* port = NULL;
211     Transcoder* transcoder = NULL;
212     int openFlags = 0;
213 
214     argumentAsString(0, path);
215     const bool isFileExist = File::isExist(path->data());
216     const bool isReadable = File::isReadable(path->data());
217 
218     if (argc == 1) {
219         if (isFileExist) {
220             return callIoFileAlreadyExistAfter(theVM, procedureName, "file already exists", argv[0]);
221         }
222 
223         // default buffer mode is Block
224         port = new BlockBufferedFileBinaryInputOutputPort(path->data(), openFlags);
225     } else {
226         argumentAsSimpleStruct(1, fileOptions);
227 
228         const bool emptyP = isEmpty(fileOptions);
229         const bool noCreateP = isNoCreate(fileOptions);
230         const bool noTruncateP = isNoTruncate(fileOptions);
231         const bool noFailP = isNoFail(fileOptions);
232 
233 //        printf("emptyP=%d noCreateP=%d noTruncateP=%d noFailP=%d\n", emptyP, noCreateP, noTruncateP, noFailP);
234 
235         if (isFileExist && emptyP) {
236             return callIoFileAlreadyExistAfter(theVM, procedureName, "file already exists", argv[0]);
237         } else if (noCreateP && noTruncateP) {
238             if (!isFileExist) {
239                 return callIoFileNotExistAfter(theVM, procedureName, "file-options no-create: file not exist", argv[0]);
240             }
241         } else if (noCreateP) {
242             if (isFileExist) {
243                 openFlags |= File::Truncate;
244             } else {
245                 return callIoFileNotExistAfter(theVM, procedureName, "file-options no-create: file not exist", argv[0]);
246             }
247         } else if (noFailP && noTruncateP) {
248             if (!isFileExist) {
249                 openFlags |= File::Truncate;
250             }
251         } else if (noFailP) {
252             openFlags |= File::Truncate;
253         } else if (noTruncateP) {
254             if (isFileExist) {
255                 return callIoFileAlreadyExistAfter(theVM, procedureName, "file-options no-trucate: file already exists", argv[0]);
256             } else {
257                 openFlags |= File::Truncate;
258             }
259         }
260 
261         if (argc == 2) {
262             port = new BlockBufferedFileBinaryInputOutputPort(path->data(), openFlags);
263         } else {
264             argumentCheckSymbol(2, bufferMode);
265 
266             if (bufferMode == Symbol::BLOCK) {
267                 port = new BlockBufferedFileBinaryInputOutputPort(path->data(), openFlags);
268             } else if (bufferMode == Symbol::LINE) {
269                 port = new LineBufferedFileBinaryInputOutputPort(path->data(), openFlags);
270             } else if (bufferMode == Symbol::NONE) {
271                 port = new FileBinaryInputOutputPort(path->data(), openFlags);
272             } else {
273                 callErrorAfter(theVM, procedureName, "invalid buffer-mode option", L1(argv[2]));
274                 return Object::Undef;
275             }
276             if (argc == 4) {
277                 argumentCheckTranscoderOrFalse(3, maybeTranscoder);
278                 if (maybeTranscoder != Object::False) {
279                     transcoder = maybeTranscoder.toTranscoder();
280                 }
281             }
282         }
283     }
284 
285     if ((port != NULL) && (MOSH_SUCCESS == port->open())) {
286         if (transcoder == NULL) {
287             return Object::makeBinaryInputOutputPort(port);
288         } else {
289             return Object::makeTextualInputOutputPort(port, transcoder);
290         }
291     } else {
292         if (port->getFile() && port->getFile()->isLastErrorAcessError()) {
293             if (isReadable) {
294                 return callIoFileReadOnlyAfter(theVM, procedureName, port->getLastErrorMessage(), argv[0]);
295             } else {
296                 return callIoFileProtectionAfter(theVM, procedureName, port->getLastErrorMessage(), argv[0]);
297             }
298         } else {
299             callErrorAfter(theVM, procedureName, port->getLastErrorMessage(), L1(argv[0]));
300             return Object::Undef;
301         }
302     }
303 }
304 
peekCharEx(VM * theVM,int argc,const Object * argv)305 Object scheme::peekCharEx(VM* theVM, int argc, const Object* argv)
306 {
307     DeclareProcedureName("peek-char");
308     checkArgumentLengthBetween(0, 1);
309     TRY_WITHOUT_DSTR
310         if (0 == argc) {
311             TextualInputPort* const port = theVM->currentInputPort().toTextualInputPort();
312             checkPortIsOpen(port, theVM->currentInputPort());
313             const ucs4char ch = port->lookaheadChar();
314             return ch == EOF ? Object::Eof : Object::makeChar(ch);
315         } else {
316             argumentAsTextualInputPort(0, textualInputPort);
317             checkPortIsOpen(textualInputPort, argv[0]);
318             const ucs4char ch = textualInputPort->lookaheadChar();
319             return ch == EOF ? Object::Eof : Object::makeChar(ch);
320         }
321     CATCH(ioError)
322         ioError.arg1 = (0 == argc) ? theVM->currentInputPort() : argv[0];
323         ioError.who = procedureName;
324         return callIOErrorAfter(theVM, ioError);
325     END_TRY
326 }
327 
getDatumEx(VM * theVM,int argc,const Object * argv)328 Object scheme::getDatumEx(VM* theVM, int argc, const Object* argv)
329 {
330     DeclareProcedureName("get-datum");
331     checkArgumentLength(1);
332     bool errorOccured = false;
333     argumentAsTextualInputPort(0, in);
334     checkPortIsOpen(in, argv[0]);
335     TRY_WITHOUT_DSTR
336         const Object object = in->getDatum(errorOccured);
337         if (errorOccured) {
338             callLexicalAndIOReadAfter(theVM, procedureName, in->error());
339             return Object::Undef;
340         }
341         return object;
342     CATCH(ioError)
343         ioError.arg1 = argv[0];
344         ioError.who = procedureName;
345         return callIOErrorAfter(theVM, ioError);
346     END_TRY
347 }
348 
getStringAllEx(VM * theVM,int argc,const Object * argv)349 Object scheme::getStringAllEx(VM* theVM, int argc, const Object* argv)
350 {
351     DeclareProcedureName("get-string-all");
352     argumentAsTextualInputPort(0, in);
353     checkPortIsOpen(in, argv[0]);
354     TRY_WITHOUT_DSTR
355         ucs4string text = in->getStringAll();
356         if (text.empty()) {
357             return Object::Undef;
358         } else {
359             return Object::makeString(text);
360         }
361     CATCH(ioError)
362         ioError.arg1 = argv[0];
363         ioError.who = procedureName;
364         return callIOErrorAfter(theVM, ioError);
365     END_TRY
366 }
367 
getStringNDEx(VM * theVM,int argc,const Object * argv)368 Object scheme::getStringNDEx(VM* theVM, int argc, const Object* argv)
369 {
370     DeclareProcedureName("get-string-n!");
371     argumentAsTextualInputPort(0, in);
372     checkPortIsOpen(in, argv[0]);
373     argumentAsString(1, dest);
374     argumentCheckExactInteger(2, start);
375     argumentCheckExactInteger(3, count);
376 
377     if (!Arithmetic::fitsU32(start)) {
378         callAssertionViolationAfter(theVM, procedureName, "start value out of range", L1(argv[2]));
379         return Object::Undef;
380     }
381 
382     if (!Arithmetic::fitsU32(count)) {
383         callAssertionViolationAfter(theVM, procedureName, "count value out of range", L1(argv[3]));
384         return Object::Undef;
385     }
386 
387     const uint32_t u32Start  = Arithmetic::toU32(start);
388     const uint32_t u32Count = Arithmetic::toU32(count);
389 
390     if ((uint32_t)dest->length() < u32Count + u32Start) {
391         callAssertionViolationAfter(theVM, procedureName, "string must be a string with at least start + count elements.", L2(argv[2], argv[3]));
392         return Object::Undef;
393     }
394 
395     TRY_WITHOUT_DSTR
396         ucs4string text = in->getString(u32Count);
397         if (text.empty()) {
398             return Object::Eof;
399         } else {
400             ucs4string& s = dest->data();
401             for (int i = 0; i < (int)text.size(); i++) {
402                 s[u32Start + i] = text[i];
403             }
404             return Bignum::makeInteger(text.size());
405         }
406     CATCH(ioError)
407         ioError.arg1 = argv[0];
408         ioError.who = procedureName;
409         return callIOErrorAfter(theVM, ioError);
410     END_TRY
411 }
412 
getCharEx(VM * theVM,int argc,const Object * argv)413 Object scheme::getCharEx(VM* theVM, int argc, const Object* argv)
414 {
415     DeclareProcedureName("get-char");
416     checkArgumentLength(1);
417     argumentAsTextualInputPort(0, textualInputPort);
418     checkPortIsOpen(textualInputPort, argv[0]);
419     TRY_WITHOUT_DSTR
420         const ucs4char ch = textualInputPort->getChar();
421         return ch == EOF ? Object::Eof : Object::makeChar(ch);
422     CATCH(ioError)
423         ioError.arg1 = argv[0];
424         ioError.who = procedureName;
425         return callIOErrorAfter(theVM, ioError);
426     END_TRY
427 }
428 
getStringNEx(VM * theVM,int argc,const Object * argv)429 Object scheme::getStringNEx(VM* theVM, int argc, const Object* argv)
430 {
431     DeclareProcedureName("get-string-n");
432     checkArgumentLength(2);
433     argumentAsTextualInputPort(0, inputPort);
434     checkPortIsOpen(inputPort, argv[0]);
435     argumentAsNonNegativeFixnum(1, size);
436 
437     if (size == 0) {
438         return "";
439     }
440 
441     TRY_WITHOUT_DSTR
442         ucs4string text = inputPort->getString(size);
443 
444         if (text.empty()) {
445             return Object::Eof;
446         } else {
447             return Object::makeString(text);
448         }
449     CATCH(ioError)
450         ioError.arg1 = argv[0];
451         ioError.who = procedureName;
452         return callIOErrorAfter(theVM, ioError);
453     END_TRY
454 }
455 
portHasPortPositionPEx(VM * theVM,int argc,const Object * argv)456 Object scheme::portHasPortPositionPEx(VM* theVM, int argc, const Object* argv)
457 {
458     DeclareProcedureName("port-has-port-position?");
459     checkArgumentLength(1);
460     argumentAsPort(0, port);
461     checkPortIsOpen(port, argv[0]);
462     return Object::makeBool(port->hasPosition());
463 }
464 
portHasSetPortPositionDPEx(VM * theVM,int argc,const Object * argv)465 Object scheme::portHasSetPortPositionDPEx(VM* theVM, int argc, const Object* argv)
466 {
467     DeclareProcedureName("port-has-set-port-position!?");
468     checkArgumentLength(1);
469     argumentAsPort(0, port);
470     checkPortIsOpen(port, argv[0]);
471     return Object::makeBool(port->hasSetPosition());
472 }
473 
setPortPositionDEx(VM * theVM,int argc,const Object * argv)474 Object scheme::setPortPositionDEx(VM* theVM, int argc, const Object* argv)
475 {
476     DeclareProcedureName("set-port-position!");
477     checkArgumentLength(2);
478     argumentAsPort(0, port);
479     checkPortIsOpen(port, argv[0]);
480     argumentAsFixnum(1, position);
481     if (port->hasSetPosition()) {
482         if (port->setPosition(position)) {
483             return Object::Undef;
484         } else {
485             return callIOInvalidPositionAfter(theVM, procedureName, "invalid port position", L2(argv[0], argv[1]), argv[1]);
486         }
487     } else {
488         callAssertionViolationAfter(theVM, procedureName, "port doesn't support set-port-position!", L1(argv[0]));
489         return Object::Undef;
490     }
491 }
492 
portPositionEx(VM * theVM,int argc,const Object * argv)493 Object scheme::portPositionEx(VM* theVM, int argc, const Object* argv)
494 {
495     DeclareProcedureName("port-position");
496     checkArgumentLength(1);
497     argumentAsPort(0, port);
498     if (port->hasPosition()) {
499         return port->position();
500     } else {
501         callAssertionViolationAfter(theVM, procedureName, "port doesn't support port-position", L1(argv[0]));
502         return Object::Undef;
503     }
504 }
505 
openBytevectorInputPortEx(VM * theVM,int argc,const Object * argv)506 Object scheme::openBytevectorInputPortEx(VM* theVM, int argc, const Object* argv)
507 {
508     DeclareProcedureName("open-bytevector-input-port");
509     checkArgumentLengthBetween(1, 2);
510     argumentAsByteVector(0, bv);
511     if (1 == argc) {
512         return Object::makeBinaryInputPort(new ByteArrayBinaryInputPort(bv->data(), bv->length()));
513     } else { // 2 == argc
514         argumentCheckTranscoderOrFalse(1, maybeTranscoder);
515         BinaryInputPort* in = new ByteArrayBinaryInputPort(bv->data(), bv->length());
516         if (maybeTranscoder.isFalse()) {
517             return Object::makeBinaryInputPort(in);
518         } else {
519             return Object::makeTextualInputPort(in, maybeTranscoder.toTranscoder());
520         }
521     }
522 }
523 
portEofPEx(VM * theVM,int argc,const Object * argv)524 Object scheme::portEofPEx(VM* theVM, int argc, const Object* argv)
525 {
526     DeclareProcedureName("port-eof?");
527     checkArgumentLength(1);
528     const Object port = argv[0];
529     TRY_WITHOUT_DSTR
530         if (port.isBinaryInputPort()) {
531             BinaryInputPort* const in = port.toBinaryInputPort();
532             checkPortIsOpen(in, port);
533             return Object::makeBool(in->lookaheadU8() == EOF);
534         } else if (port.isBinaryInputOutputPort()) {
535             BinaryInputOutputPort* const inout = port.toBinaryInputOutputPort();
536             checkPortIsOpen(inout, port);
537             return Object::makeBool(inout->lookaheadU8() == EOF);
538         } else if (port.isTextualInputPort()) {
539             TextualInputPort* const in = port.toTextualInputPort();
540             checkPortIsOpen(in, port);
541             return Object::makeBool(in->lookaheadChar() == EOF);
542         } else if (port.isTextualInputOutputPort()) {
543             TextualInputOutputPort* const inout = port.toTextualInputOutputPort();
544             checkPortIsOpen(inout, port);
545             return Object::makeBool(inout->lookaheadChar() == EOF);
546         } else {
547             callWrongTypeOfArgumentViolationAfter(theVM, procedureName, "port", port, L1(port));
548             return Object::Undef;
549         }
550     CATCH(ioError)
551         ioError.arg1 = argv[0];
552         ioError.who = procedureName;
553         return callIOErrorAfter(theVM, ioError);
554     END_TRY
555 }
556 
putBytevectorEx(VM * theVM,int argc,const Object * argv)557 Object scheme::putBytevectorEx(VM* theVM, int argc, const Object* argv)
558 {
559     DeclareProcedureName("put-bytevector");
560     checkArgumentLengthBetween(2, 4);
561     argumentAsBinaryOutputPort(0, outputPort);
562     checkPortIsOpen(outputPort, argv[0]);
563     argumentAsByteVector(1, bv);
564     TRY_WITHOUT_DSTR
565         if (argc < 3) {
566             outputPort->putByteVector(bv);
567             return Object::Undef;
568         }
569 
570         argumentCheckExactInteger(2, startObj);
571         int start;
572         if (startObj.isFixnum()) {
573             start = startObj.toFixnum();
574         } else { // startObj.isBignum()
575             start = startObj.toBignum()->toS32();
576         }
577         if (argc < 4) {
578             outputPort->putByteVector(bv, start);
579             return Object::Undef;
580         }
581 
582         argumentCheckExactInteger(3, countObj);
583         int count;
584         if (countObj.isFixnum()) {
585             count = countObj.toFixnum();
586         } else { // countObj.isBignum()
587             count = countObj.toBignum()->toS32();
588         }
589         outputPort->putByteVector(bv, start, count);
590         return Object::Undef;
591     CATCH(ioError)
592         ioError.arg1 = argv[0];
593         ioError.who = procedureName;
594         return callIOErrorAfter(theVM, ioError);
595     END_TRY
596 }
597 
putCharEx(VM * theVM,int argc,const Object * argv)598 Object scheme::putCharEx(VM* theVM, int argc, const Object* argv)
599 {
600     DeclareProcedureName("put-char");
601     checkArgumentLength(2);
602     argumentAsTextualOutputPort(0, textualOutputPort);
603     checkPortIsOpen(textualOutputPort, argv[0]);
604     argumentAsChar(1, ch);
605     TRY_WITHOUT_DSTR
606         textualOutputPort->putChar(ch);
607         return Object::Undef;
608     CATCH(ioError)
609         ioError.arg1 = argv[0];
610         ioError.who = procedureName;
611         return callIOErrorAfter(theVM, ioError);
612     END_TRY
613 }
614 
putDatumEx(VM * theVM,int argc,const Object * argv)615 Object scheme::putDatumEx(VM* theVM, int argc, const Object* argv)
616 {
617     DeclareProcedureName("put-datum");
618     checkArgumentLength(2);
619     argumentAsTextualOutputPort(0, textualOutputPort);
620     checkPortIsOpen(textualOutputPort, argv[0]);
621     TRY_WITHOUT_DSTR
622         textualOutputPort->putDatum(theVM, argv[1]);
623         return Object::Undef;
624     CATCH(ioError)
625         ioError.arg1 = argv[0];
626         ioError.who = procedureName;
627         return callIOErrorAfter(theVM, ioError);
628     END_TRY
629 }
630 
631 
outputPortPEx(VM * theVM,int argc,const Object * argv)632 Object scheme::outputPortPEx(VM* theVM, int argc, const Object* argv)
633 {
634     DeclareProcedureName("output-port?");
635     checkArgumentLength(1);
636     return Object::makeBool(argv[0].isOutputPort());
637 }
638 
faslWriteEx(VM * theVM,int argc,const Object * argv)639 Object scheme::faslWriteEx(VM* theVM, int argc, const Object* argv)
640 {
641     DeclareProcedureName("fasl-write");
642     checkArgumentLength(2);
643     argumentAsBinaryOutputPort(1, outputPort);
644     checkPortIsOpen(outputPort, argv[0]);
645     FaslWriter writer(outputPort);
646     TRY_WITHOUT_DSTR
647         writer.put(argv[0]);
648     CATCH(ioError)
649         ioError.arg1 = argv[1];
650         ioError.who = procedureName;
651         return callIOErrorAfter(theVM, ioError);
652     END_TRY
653     return Object::Undef;
654 }
655 
faslReadEx(VM * theVM,int argc,const Object * argv)656 Object scheme::faslReadEx(VM* theVM, int argc, const Object* argv)
657 {
658     DeclareProcedureName("fasl-read");
659     checkArgumentLength(1);
660     argumentAsBinaryInputPort(0, inputPort);
661     checkPortIsOpen(inputPort, argv[0]);
662     FaslReader reader(theVM, inputPort);
663     return reader.get();
664 }
665 
getLineEx(VM * theVM,int argc,const Object * argv)666 Object scheme::getLineEx(VM* theVM, int argc, const Object* argv)
667 {
668     DeclareProcedureName("get-line");
669     checkArgumentLength(1);
670     argumentAsTextualInputPort(0, inputPort);
671     checkPortIsOpen(inputPort, argv[0]);
672     TRY_WITHOUT_DSTR
673         return inputPort->getLine();
674     CATCH(ioError)
675         ioError.arg1 = argv[0];
676         ioError.who = procedureName;
677         return callIOErrorAfter(theVM, ioError);
678     END_TRY
679 }
680 
closePortEx(VM * theVM,int argc,const Object * argv)681 Object scheme::closePortEx(VM* theVM, int argc, const Object* argv)
682 {
683     DeclareProcedureName("close-port");
684     checkArgumentLength(1);
685     argumentAsPort(0, port);
686     TRY_WITHOUT_DSTR
687         port->close();
688 //         if (port.isBinaryOutputPort()) {
689 //             port.toBinaryOutputPort()->close();
690 //         } else if (port.isBinaryInputOutputPort()) {
691 //             port.toBinaryInputOutputPort()->close();
692 //         } else if (port.isTextualOutputPort()) {
693 //             port.toTextualOutputPort()->close();
694 //         } else if (port.isTextualInputOutputPort()) {
695 //             port.toTextualInputOutputPort()->close();
696 //         } else if (port.isBinaryInputPort()) {
697 //             port.toBinaryInputPort()->close();
698 //         } else if (port.isTextualInputPort()) {
699 //             port.toTextualInputPort()->close();
700 //         } else {
701 //             callAssertionViolationAfter(theVM, procedureName, "port required", L1(port));
702 //         }
703     CATCH(ioError)
704         ioError.arg1 = argv[0];
705         ioError.who = procedureName;
706         return callIOErrorAfter(theVM, ioError);
707     END_TRY
708     const Object outPort = argv[0];
709     if (outPort.isBinaryOutputPort() || outPort.isBinaryInputOutputPort() ||
710         outPort.isTextualOutputPort() || outPort.isTextualInputOutputPort()) {
711         theVM->unregisterPort(outPort);
712     }
713     return Object::Undef;
714 }
715 
standardLibraryPathEx(VM * theVM,int argc,const Object * argv)716 Object scheme::standardLibraryPathEx(VM* theVM, int argc, const Object* argv)
717 {
718     DeclareProcedureName("standard-library-path");
719     checkArgumentLength(0);
720 #ifdef MONA
721     return Object::makeString(UC("/APPS/MOSH/LIB"));
722 #else
723     return Object::makeString(UC(MOSH_LIB_PATH));
724 #endif
725 }
726 
lookaheadCharEx(VM * theVM,int argc,const Object * argv)727 Object scheme::lookaheadCharEx(VM* theVM, int argc, const Object* argv)
728 {
729     DeclareProcedureName("lookahead-char");
730     checkArgumentLength(1);
731     argumentAsTextualInputPort(0, textualInputPort);
732     checkPortIsOpen(textualInputPort, argv[0]);
733     TRY_WITHOUT_DSTR
734         ucs4char ch;
735         ch = textualInputPort->lookaheadChar();
736         return ch == EOF ? Object::Eof : Object::makeChar(ch);
737     CATCH(ioError)
738         ioError.arg1 = argv[0];
739         ioError.who = procedureName;
740         return callIOErrorAfter(theVM, ioError);
741     END_TRY
742 }
743 
currentErrorPortEx(VM * theVM,int argc,const Object * argv)744 Object scheme::currentErrorPortEx(VM* theVM, int argc, const Object* argv)
745 {
746     DeclareProcedureName("current-error-port");
747     checkArgumentLength(0);
748     return theVM->currentErrorPort();
749 }
750 
sysDisplayEx(VM * theVM,int argc,const Object * argv)751 Object scheme::sysDisplayEx(VM* theVM, int argc, const Object* argv)
752 {
753     DeclareProcedureName("display");
754     checkArgumentLengthBetween(1, 2);
755     const Object obj = argv[0];
756     TRY_WITHOUT_DSTR
757         if (1 == argc) {
758             TextualOutputPort* const out = theVM->currentOutputPort().toTextualOutputPort();
759             checkPortIsOpen(out, theVM->currentOutputPort());
760             out->display(theVM, obj);
761             // todo
762             out->flush();
763         } else {
764             argumentAsTextualOutputPort(1, textualOutputPort);
765             checkPortIsOpen(textualOutputPort, argv[1]);
766             textualOutputPort->display(theVM, obj);
767             // todo
768 //            textualOutputPort->flush();
769         }
770         return Object::Undef;
771     CATCH(ioError)
772         ioError.arg1 = argv[0];
773         ioError.who = procedureName;
774         return callIOErrorAfter(theVM, ioError);
775     END_TRY
776 }
777 
writeCharEx(VM * theVM,int argc,const Object * argv)778 Object scheme::writeCharEx(VM* theVM, int argc, const Object* argv)
779 {
780     DeclareProcedureName("write-char");
781     checkArgumentLengthBetween(1, 2);
782     argumentAsChar(0, ch);
783     TRY_WITHOUT_DSTR
784         if (1 == argc) {
785             TextualOutputPort* const out = theVM->currentOutputPort().toTextualOutputPort();
786             checkPortIsOpen(out, theVM->currentOutputPort());
787             out->putChar(ch);
788         } else {
789             argumentAsTextualOutputPort(1, textualOutputPort);
790             checkPortIsOpen(textualOutputPort, argv[1]);
791             textualOutputPort->putChar(ch);
792         }
793         return Object::Undef;
794     CATCH(ioError)
795         ioError.arg1 = argv[0];
796         ioError.who = procedureName;
797         return callIOErrorAfter(theVM, ioError);
798     END_TRY
799 }
800 
eofObjectPEx(VM * theVM,int argc,const Object * argv)801 Object scheme::eofObjectPEx(VM* theVM, int argc, const Object* argv)
802 {
803     DeclareProcedureName("eof-object?");
804     checkArgumentLength(1);
805     return Object::makeBool(argv[0].isEof());
806 }
807 
readCharEx(VM * theVM,int argc,const Object * argv)808 Object scheme::readCharEx(VM* theVM, int argc, const Object* argv)
809 {
810     DeclareProcedureName("read-char");
811     checkArgumentLengthBetween(0, 1);
812     TRY_WITHOUT_DSTR
813         if (0 == argc) {
814             TextualInputPort* const in = theVM->currentInputPort().toTextualInputPort();
815             checkPortIsOpen(in, theVM->currentInputPort());
816             const ucs4char ch = in->getChar();
817             return ch == EOF ? Object::Eof : Object::makeChar(ch);
818         } else {
819             argumentAsTextualInputPort(0, textualInputPort);
820             checkPortIsOpen(textualInputPort, argv[0]);
821             const ucs4char ch = textualInputPort->getChar();
822             return ch == EOF ? Object::Eof : Object::makeChar(ch);
823         }
824     CATCH(ioError)
825         ioError.arg1 = (0 == argc) ? theVM->currentInputPort() : argv[0];
826         ioError.who = procedureName;
827         return callIOErrorAfter(theVM, ioError);
828     END_TRY
829 }
830 
readEx(VM * theVM,int argc,const Object * argv)831 Object scheme::readEx(VM* theVM, int argc, const Object* argv)
832 {
833     DeclareProcedureName("read");
834     checkArgumentLengthBetween(0, 1);
835 
836     bool errorOccured = false;
837     TextualInputPort* inputPort = NULL;
838     if (0 == argc) {
839         inputPort = theVM->currentInputPort().toTextualInputPort();
840         checkPortIsOpen(inputPort, theVM->currentInputPort());
841     } else {
842         argumentAsTextualInputPort(0, textualInputPort);
843         checkPortIsOpen(textualInputPort, argv[0]);
844         inputPort = textualInputPort;
845     }
846 
847     TRY_WITHOUT_DSTR
848         const Object object = inputPort->getDatum(errorOccured);
849         if (errorOccured) {
850             callLexicalAndIOReadAfter(theVM, procedureName, inputPort->error());
851             return Object::Undef;
852         }
853         return object;
854     CATCH(ioError)
855         ioError.arg1 = (0 == argc) ? theVM->currentInputPort() : argv[0];
856         ioError.who = procedureName;
857         return callIOErrorAfter(theVM, ioError);
858     END_TRY
859 }
860 
openStringInputPortEx(VM * theVM,int argc,const Object * argv)861 Object scheme::openStringInputPortEx(VM* theVM, int argc, const Object* argv)
862 {
863     DeclareProcedureName("string-input-port");
864     argumentAsString(0, text);
865     return Object::makeStringInputPort(text->data());
866 }
867 
openOutputStringEx(VM * theVM,int argc,const Object * argv)868 Object scheme::openOutputStringEx(VM* theVM, int argc, const Object* argv)
869 {
870     return  Object::makeStringOutputPort();
871 }
872 
sysPortSeekEx(VM * theVM,int argc,const Object * argv)873 Object scheme::sysPortSeekEx(VM* theVM, int argc, const Object* argv)
874 {
875     // todo
876     return Object::UnBound;
877 }
878 
closeOutputPortEx(VM * theVM,int argc,const Object * argv)879 Object scheme::closeOutputPortEx(VM* theVM, int argc, const Object* argv)
880 {
881     DeclareProcedureName("close-output-port");
882     checkArgumentLength(1);
883 
884     if (argv[0].isTextualOutputPort() || argv[0].isBinaryOutputPort()) {
885         argumentAsPort(0, port);
886         port->close();
887         return Object::Undef;
888     } else {
889         callAssertionViolationAfter(theVM, procedureName, "output port required", L1(argv[0]));
890         return Object::Undef;
891     }
892 }
893 
closeInputPortEx(VM * theVM,int argc,const Object * argv)894 Object scheme::closeInputPortEx(VM* theVM, int argc, const Object* argv)
895 {
896     DeclareProcedureName("close-input-port");
897     checkArgumentLength(1);
898     if (argv[0].isTextualInputPort() || argv[0].isBinaryInputPort()) {
899         argumentAsPort(0, port);
900         port->close();
901         return Object::Undef;
902     } else {
903         callAssertionViolationAfter(theVM, procedureName, "input port required", L1(argv[0]));
904         return Object::Undef;
905     }
906 }
907 
getOutputStringEx(VM * theVM,int argc,const Object * argv)908 Object scheme::getOutputStringEx(VM* theVM, int argc, const Object* argv)
909 {
910     DeclareProcedureName("get-output-string");
911     checkArgumentLength(1);
912     argumentAsTextualOutputPort(0, textualOutputPort);
913     StringTextualOutputPort* p = reinterpret_cast<StringTextualOutputPort*>(textualOutputPort);
914     const Object ret = Object::makeString(p->getString());
915     p->reset();
916     return ret;
917 }
918 
deleteFileEx(VM * theVM,int argc,const Object * argv)919 Object scheme::deleteFileEx(VM* theVM, int argc, const Object* argv)
920 {
921     DeclareProcedureName("delete-file");
922     checkArgumentLength(1);
923     argumentAsString(0, text);
924 #ifdef MONA
925     if (monapi_file_delete(text->data().ascii_c_str()) != M_OK) {
926         callIoFileNameErrorAfter(theVM, procedureName,
927                                  "can't delete file",
928                                  argv[0]);
929         return Object::Undef;
930     } else {
931         return Object::Undef;
932     }
933 #else
934     if (-1 == unlink(text->data().ascii_c_str())) {
935         callIoFileNameErrorAfter(theVM, procedureName,
936                                  "can't delete file",
937                                  argv[0]);
938         return Object::Undef;
939     } else {
940         return Object::Undef;
941     }
942 #endif
943 }
944 
fileExistsPEx(VM * theVM,int argc,const Object * argv)945 Object scheme::fileExistsPEx(VM* theVM, int argc, const Object* argv)
946 {
947     DeclareProcedureName("file-exists?");
948     checkArgumentLength(1);
949     argumentAsString(0, path);
950     return Object::makeBool(File::isExist(path->data()));
951 }
952 
953 // todo cleanup
formatEx(VM * theVM,int argc,const Object * argv)954 Object scheme::formatEx(VM* theVM, int argc, const Object* argv)
955 {
956     DeclareProcedureName("format");
957     TRY_WITHOUT_DSTR
958         const Object arg1 = argv[0];
959         if (arg1.isTextualOutputPort()) {
960             checkArgumentLengthAtLeast(2);
961             argumentAsTextualOutputPort(0, textualOutputPort);
962             checkPortIsOpen(textualOutputPort, argv[0]);
963             argumentAsString(1, formatString);
964             Object lst = Object::Nil;
965             for (int i = argc - 1; i >= 2; i--) {
966                 lst = Object::cons(argv[i], lst);
967             }
968             textualOutputPort->format(theVM, formatString->data(), lst);
969             if (textualOutputPort->isErrorOccured()) {
970                 callAssertionViolationAfter(theVM, procedureName,
971                                             textualOutputPort->errorMessage(),
972                                             textualOutputPort->irritants());
973                 return Object::Undef;
974             } else {
975                 return Object::Undef;
976             }
977         } else if (arg1.isTrue()) {
978             checkArgumentLengthAtLeast(2);
979             argumentAsString(1, formatString);
980             Object lst = Object::Nil;
981             for (int i = argc - 1; i >= 2; i--) {
982                 lst = Object::cons(argv[i], lst);
983             }
984             TextualOutputPort* const outputPort = theVM->currentOutputPort().toTextualOutputPort();
985             checkPortIsOpen(outputPort, theVM->currentOutputPort());
986             outputPort->format(theVM, formatString->data(), lst);
987             if (outputPort->isErrorOccured()) {
988                 callAssertionViolationAfter(theVM, procedureName,
989                                             outputPort->errorMessage(),
990                                             outputPort->irritants());
991                 return Object::Undef;
992             } else {
993                 return Object::Undef;
994             }
995         } else if (arg1.isFalse()) {
996             checkArgumentLengthAtLeast(2);
997             argumentAsString(1, formatString);
998             const Object port = Object::makeStringOutputPort();
999             StringTextualOutputPort* const p = static_cast<StringTextualOutputPort*>(port.toTextualOutputPort());
1000             Object lst = Object::Nil;
1001             for (int i = argc - 1; i >= 2; i--) {
1002                 lst = Object::cons(argv[i], lst);
1003             }
1004 
1005             p->format(theVM, formatString->data(), lst);
1006             if (p->isErrorOccured()) {
1007                 callAssertionViolationAfter(theVM, procedureName,
1008                                             p->errorMessage(),
1009                                             p->irritants());
1010                 return Object::Undef;
1011             } else {
1012                 return Object::makeString(p->getString());
1013             }
1014         } else if (arg1.isString()) {
1015             const Object port = Object::makeStringOutputPort();
1016             StringTextualOutputPort* const p = static_cast<StringTextualOutputPort*>(port.toTextualOutputPort());
1017             Object lst = Object::Nil;
1018             for (int i = argc - 1; i >= 1; i--) {
1019                 lst = Object::cons(argv[i], lst);
1020             }
1021             p->format(theVM, arg1.toString()->data(), lst);
1022             if (p->isErrorOccured()) {
1023                 callAssertionViolationAfter(theVM, procedureName,
1024                                             p->errorMessage(),
1025                                             p->irritants());
1026                 return Object::Undef;
1027             } else {
1028                 return Object::makeString(p->getString());
1029             }
1030         } else {
1031             callAssertionViolationAfter(theVM, procedureName, "port and format string required");
1032             return Object::Undef;
1033         }
1034     CATCH(ioError)
1035         ioError.arg1 = argv[0];
1036         ioError.who = procedureName;
1037         return callIOErrorAfter(theVM, ioError);
1038     END_TRY
1039 }
1040 
writeEx(VM * theVM,int argc,const Object * argv)1041 Object scheme::writeEx(VM* theVM, int argc, const Object* argv)
1042 {
1043     DeclareProcedureName("write");
1044     checkArgumentLengthBetween(1, 2);
1045     const Object obj = argv[0];
1046     bool isSharedAware = false;
1047     TRY_WITHOUT_DSTR
1048         if (1 == argc) {
1049             TextualOutputPort* const out = theVM->currentOutputPort().toTextualOutputPort();
1050             checkPortIsOpen(out, theVM->currentOutputPort());
1051             out->putDatum(theVM, obj, isSharedAware);
1052 
1053         } else {
1054             argumentAsTextualOutputPort(1, textualOutputPort);
1055             checkPortIsOpen(textualOutputPort, argv[1]);
1056             textualOutputPort->putDatum(theVM, obj, isSharedAware);
1057         }
1058         return Object::Undef;
1059     CATCH(ioError)
1060         ioError.arg1 = argv[0];
1061         ioError.who = procedureName;
1062         return callIOErrorAfter(theVM, ioError);
1063     END_TRY
1064 }
1065 
fileTostringEx(VM * theVM,int argc,const Object * argv)1066 Object scheme::fileTostringEx(VM* theVM, int argc, const Object* argv)
1067 {
1068     DeclareProcedureName("file->string");
1069     checkArgumentLength(1);
1070     argumentAsString(0, path);
1071     if (File::isExist(path->data())) {
1072         ucs4string ret;
1073         TextualInputPort* in = new TranscodedTextualInputPort(new BufferedFileBinaryInputPort(path->data()), createNativeTranscoder());
1074         for (ucs4char ch = in->getChar(); ch != EOF; ch = in->getChar()) {
1075             ret += ch;
1076         }
1077         return ret;
1078     } else {
1079         return "";
1080     }
1081 }
1082 
writeSsEx(VM * theVM,int argc,const Object * argv)1083 Object scheme::writeSsEx(VM* theVM, int argc, const Object* argv)
1084 {
1085     DeclareProcedureName("write/ss");
1086     checkArgumentLengthBetween(1, 2);
1087     bool isSharedAware = true;
1088     const Object obj = argv[0];
1089     TRY_WITHOUT_DSTR
1090         if (1 == argc) {
1091             TextualOutputPort* const out = theVM->currentOutputPort().toTextualOutputPort();
1092             checkPortIsOpen(out, theVM->currentOutputPort());
1093             out->putDatum(theVM, obj, isSharedAware);
1094 
1095         } else {
1096             argumentAsTextualOutputPort(1, textualOutputPort);
1097             checkPortIsOpen(textualOutputPort, argv[1]);
1098             textualOutputPort->putDatum(theVM, obj, isSharedAware);
1099         }
1100         return Object::Undef;
1101     CATCH(ioError)
1102         ioError.arg1 = argv[0];
1103         ioError.who = procedureName;
1104         return callIOErrorAfter(theVM, ioError);
1105     END_TRY
1106 }
1107 
makeCustomBinaryInputPortEx(VM * theVM,int argc,const Object * argv)1108 Object scheme::makeCustomBinaryInputPortEx(VM* theVM, int argc, const Object* argv)
1109 {
1110     DeclareProcedureName("make-custom-binary-input-port");
1111     checkArgumentLength(5);
1112 
1113     argumentAsString(0, id);
1114     argumentCheckProcedure(1, readProc);
1115     argumentCheckProcedureOrFalse(2, getPositionProc);
1116     argumentCheckProcedureOrFalse(3, setPositionProc);
1117     argumentCheckProcedureOrFalse(4, closeProc);
1118 
1119     return Object::makeCustomBinaryInputPort(theVM, id->data(), readProc, getPositionProc, setPositionProc, closeProc);
1120 }
1121 
makeCustomBinaryOutputPortEx(VM * theVM,int argc,const Object * argv)1122 Object scheme::makeCustomBinaryOutputPortEx(VM* theVM, int argc, const Object* argv)
1123 {
1124     DeclareProcedureName("make-custom-binary-output-port");
1125     checkArgumentLength(5);
1126 
1127     argumentAsString(0, id);
1128     argumentCheckProcedure(1, writeDProc);
1129     argumentCheckProcedureOrFalse(2, getPositionProc);
1130     argumentCheckProcedureOrFalse(3, setPositionDProc);
1131     argumentCheckProcedureOrFalse(4, closeProc);
1132 
1133     return Object::makeCustomBinaryOutputPort(theVM, id->data(), writeDProc, getPositionProc, setPositionDProc, closeProc);
1134 }
1135 
makeCustomTextualInputPortEx(VM * theVM,int argc,const Object * argv)1136 Object scheme::makeCustomTextualInputPortEx(VM* theVM, int argc, const Object* argv)
1137 {
1138     DeclareProcedureName("make-custom-textual-input-port");
1139     checkArgumentLength(5);
1140 
1141     argumentAsString(0, id);
1142     argumentCheckProcedure(1, readProc);
1143     argumentCheckProcedureOrFalse(2, getPositionProc);
1144     argumentCheckProcedureOrFalse(3, setPositionProc);
1145     argumentCheckProcedureOrFalse(4, closeProc);
1146 
1147     return Object::makeCustomTextualInputPort(theVM, id->data(), readProc, getPositionProc, setPositionProc, closeProc);
1148 }
1149 
makeCustomTextualOutputPortEx(VM * theVM,int argc,const Object * argv)1150 Object scheme::makeCustomTextualOutputPortEx(VM* theVM, int argc, const Object* argv)
1151 {
1152     DeclareProcedureName("make-custom-textual-output-port");
1153     checkArgumentLength(5);
1154 
1155     argumentAsString(0, id);
1156     argumentCheckProcedure(1, writeDProc);
1157     argumentCheckProcedureOrFalse(2, getPositionProc);
1158     argumentCheckProcedureOrFalse(3, setPositionDProc);
1159     argumentCheckProcedureOrFalse(4, closeProc);
1160 
1161     return Object::makeCustomTextualOutputPort(theVM, id->data(), writeDProc, getPositionProc, setPositionDProc, closeProc);
1162 }
1163 
getU8Ex(VM * theVM,int argc,const Object * argv)1164 Object scheme::getU8Ex(VM* theVM, int argc, const Object* argv)
1165 {
1166     DeclareProcedureName("get-u8");
1167     checkArgumentLength(1);
1168     argumentAsBinaryInputPort(0, binaryInputPort);
1169     checkPortIsOpen(binaryInputPort, argv[0]);
1170     const int b = binaryInputPort->getU8();
1171     if (EOF == b) {
1172         return Object::Eof;
1173     } else {
1174         return Object::makeFixnum(b);
1175     }
1176 }
1177 
lookaheadU8Ex(VM * theVM,int argc,const Object * argv)1178 Object scheme::lookaheadU8Ex(VM* theVM, int argc, const Object* argv)
1179 {
1180     DeclareProcedureName("lookahead-u8");
1181     checkArgumentLength(1);
1182     argumentAsBinaryInputPort(0, binaryInputPort);
1183     checkPortIsOpen(binaryInputPort, argv[0]);
1184     const int b = binaryInputPort->lookaheadU8();
1185     if (EOF == b) {
1186         return Object::Eof;
1187     } else {
1188         return Object::makeFixnum(b);
1189     }
1190 }
1191 
getBytevectorNEx(VM * theVM,int argc,const Object * argv)1192 Object scheme::getBytevectorNEx(VM* theVM, int argc, const Object* argv)
1193 {
1194     DeclareProcedureName("get-bytevector-n");
1195     checkArgumentLength(2);
1196 
1197     argumentAsBinaryInputPort(0, binaryInputPort);
1198     checkPortIsOpen(binaryInputPort, argv[0]);
1199     argumentCheckExactInteger(1, count);
1200     if (!Arithmetic::fitsU32(count)) {
1201         callAssertionViolationAfter(theVM, procedureName, "value out of range", L1(argv[1]));
1202         return Object::Undef;
1203     }
1204 
1205     const uint32_t u32Count = Arithmetic::toU32(count);
1206 
1207     if (u32Count == 0) {
1208         return Object::makeByteVector(0);
1209     }
1210 
1211     uint8_t* buffer = allocatePointerFreeU8Array(u32Count);
1212     bool isErrorOccured = false;
1213     const uint32_t ret = static_cast<uint32_t>(binaryInputPort->readBytes(buffer, u32Count, isErrorOccured));
1214     if (isErrorOccured) {
1215         callAssertionViolationAfter(theVM, procedureName, "read error");
1216         return Object::Undef;
1217     } else if (ret == 0) {
1218         return Object::Eof;
1219     } else {
1220         return Object::makeByteVector(new ByteVector(ret, buffer));
1221     }
1222 }
1223 
getBytevectorAllEx(VM * theVM,int argc,const Object * argv)1224 Object scheme::getBytevectorAllEx(VM* theVM, int argc, const Object* argv)
1225 {
1226     DeclareProcedureName("get-bytevector-all");
1227     checkArgumentLength(1);
1228 
1229     argumentAsBinaryInputPort(0, binaryInputPort);
1230     checkPortIsOpen(binaryInputPort, argv[0]);
1231     bool isErrorOccured = false;
1232     uint8_t* dest;
1233     const int64_t ret = binaryInputPort->readAll(&dest, isErrorOccured);
1234     if (isErrorOccured) {
1235         callAssertionViolationAfter(theVM, procedureName, "read error");
1236         return Object::Undef;
1237     } else if (ret == 0) {
1238         return Object::Eof;
1239     } else {
1240         MOSH_ASSERT(isInSize_t(ret));
1241         return Object::makeByteVector(new ByteVector(static_cast<size_t>(ret), dest));
1242     }
1243 }
1244 
getBytevectorSomeEx(VM * theVM,int argc,const Object * argv)1245 Object scheme::getBytevectorSomeEx(VM* theVM, int argc, const Object* argv)
1246 {
1247     DeclareProcedureName("get-bytevector-some");
1248     checkArgumentLength(1);
1249 
1250     argumentAsBinaryInputPort(0, binaryInputPort);
1251     checkPortIsOpen(binaryInputPort, argv[0]);
1252     bool isErrorOccured = false;
1253     uint8_t* dest;
1254     const int64_t ret = binaryInputPort->readSome(&dest, isErrorOccured);
1255     if (isErrorOccured) {
1256         callAssertionViolationAfter(theVM, procedureName, "read error");
1257         return Object::Undef;
1258     } else if (ret == 0) {
1259         return Object::Eof;
1260     } else {
1261         MOSH_ASSERT(isInSize_t(ret));
1262         return Object::makeByteVector(new ByteVector(static_cast<size_t>(ret), dest));
1263     }
1264 }
1265 
getBytevectorNDEx(VM * theVM,int argc,const Object * argv)1266 Object scheme::getBytevectorNDEx(VM* theVM, int argc, const Object* argv)
1267 {
1268     DeclareProcedureName("get-bytevector-n!");
1269     checkArgumentLength(4);
1270 
1271     argumentAsBinaryInputPort(0, binaryInputPort);
1272     checkPortIsOpen(binaryInputPort, argv[0]);
1273     argumentAsByteVector(1, bv);
1274     argumentCheckExactInteger(2, start);
1275     argumentCheckExactInteger(3, count);
1276 
1277     if (!Arithmetic::fitsU32(start)) {
1278         callAssertionViolationAfter(theVM, procedureName, "start value out of range", L1(argv[2]));
1279         return Object::Undef;
1280     }
1281 
1282     if (!Arithmetic::fitsU32(count)) {
1283         callAssertionViolationAfter(theVM, procedureName, "count value out of range", L1(argv[3]));
1284         return Object::Undef;
1285     }
1286 
1287     const uint32_t u32Start = Arithmetic::toU32(start);
1288     const uint32_t u32Count = Arithmetic::toU32(count);
1289 
1290     if (bv->length() < u32Count + u32Start) {
1291         callAssertionViolationAfter(theVM, procedureName, "bytevector must be a bytevector with at least start + count elements.", L2(argv[2], argv[3]));
1292         return Object::Undef;
1293     }
1294 
1295     bool isErrorOccured = false;
1296     const uint32_t ret = static_cast<uint32_t>(binaryInputPort->readBytes(bv->data() + u32Start, u32Count, isErrorOccured));
1297     if (isErrorOccured) {
1298         callAssertionViolationAfter(theVM, procedureName, "read error");
1299         return Object::Undef;
1300     } else if (ret == 0) {
1301         return Object::Eof;
1302     } else {
1303         return Bignum::makeInteger(ret);
1304     }
1305 }
1306 
transcodedPortEx(VM * theVM,int argc,const Object * argv)1307 Object scheme::transcodedPortEx(VM* theVM, int argc, const Object* argv)
1308 {
1309     DeclareProcedureName("transcoded-port");
1310     checkArgumentLength(2);
1311 
1312     argumentAsTranscoder(1, transcoder);
1313     const Object port = argv[0];
1314     if (port.isBinaryInputPort()) {
1315         BinaryInputPort* const in = port.toBinaryInputPort();
1316         in->pseudoClose();
1317         return Object::makeTextualInputPort(in, transcoder);
1318     } else if (port.isBinaryOutputPort()) {
1319         BinaryOutputPort* const out = port.toBinaryOutputPort();
1320         out->pseudoClose();
1321         return Object::makeTextualOutputPort(out, transcoder);
1322     } else if (port.isBinaryInputOutputPort()) {
1323         BinaryInputOutputPort* const inout = port.toBinaryInputOutputPort();
1324         inout->pseudoClose();
1325         return Object::makeTextualInputOutputPort(inout, transcoder);
1326     } else {
1327         callWrongTypeOfArgumentViolationAfter(theVM, procedureName, "binary port", port, L1(port));
1328         return Object::Undef;
1329     }
1330 }
1331 
latin1CodecEx(VM * theVM,int argc,const Object * argv)1332 Object scheme::latin1CodecEx(VM* theVM, int argc, const Object* argv)
1333 {
1334     DeclareProcedureName("latin-1-codec");
1335     checkArgumentLength(0);
1336     return Object::makeCodec(new Latin1Codec());
1337 }
1338 
utf8CodecEx(VM * theVM,int argc,const Object * argv)1339 Object scheme::utf8CodecEx(VM* theVM, int argc, const Object* argv)
1340 {
1341     DeclareProcedureName("utf-8-codec");
1342     checkArgumentLength(0);
1343     return Object::makeCodec(new UTF8Codec());
1344 }
1345 
utf16CodecEx(VM * theVM,int argc,const Object * argv)1346 Object scheme::utf16CodecEx(VM* theVM, int argc, const Object* argv)
1347 {
1348     DeclareProcedureName("utf-16-codec");
1349     checkArgumentLength(0);
1350     return Object::makeCodec(new UTF16Codec);
1351 }
1352 
nativeEolStyleEx(VM * theVM,int argc,const Object * argv)1353 Object scheme::nativeEolStyleEx(VM* theVM, int argc, const Object* argv)
1354 {
1355     DeclareProcedureName("native-eol-style");
1356     checkArgumentLength(0);
1357 
1358 #if LINE_FEED_CODE_LF
1359     return Symbol::LF;
1360 #elif LINE_FEED_CODE_CRLF
1361     return Symbol::CRLF;
1362 #elif LINE_FEED_CODE_CR
1363     return Symbol::CR;
1364 #else
1365     MOSH_FATAL("not found platform native eol style\n");
1366 #endif
1367 }
1368 
makeTranscoderEx(VM * theVM,int argc,const Object * argv)1369 Object scheme::makeTranscoderEx(VM* theVM, int argc, const Object* argv)
1370 {
1371     DeclareProcedureName("make-transcoder");
1372     checkArgumentLengthBetween(1, 3);
1373 
1374     argumentAsCodec(0, codec);
1375     if (argc == 1) {
1376         return Object::makeTranscoder(codec);
1377     }
1378     argumentCheckSymbol(1, style);
1379     EolStyle eolStyle;
1380     if (!Transcoder::validateEolStyle(style, eolStyle)) {
1381         callAssertionViolationAfter(theVM, procedureName, "invalid eol-style", L1(argv[1]));
1382         return Object::Undef;
1383     }
1384     if (argc == 2) {
1385         return Object::makeTranscoder(codec, eolStyle);
1386     }
1387     argumentCheckSymbol(2, errorHandlingMode);
1388     ErrorHandlingMode mode;
1389     if (!Transcoder::validateErrorHandlingMode(errorHandlingMode, mode)) {
1390         callAssertionViolationAfter(theVM, procedureName, "invalid error-handling-mode", L1(argv[2]));
1391         return Object::Undef;
1392     }
1393     return Object::makeTranscoder(codec, eolStyle, mode);
1394 }
1395 
nativeTranscoderEx(VM * theVM,int argc,const Object * argv)1396 Object scheme::nativeTranscoderEx(VM* theVM, int argc, const Object* argv)
1397 {
1398     DeclareProcedureName("native-transcoder");
1399     checkArgumentLength(0);
1400     return Object::makeTranscoder(createNativeTranscoder());
1401 }
1402 
transcoderCodecEx(VM * theVM,int argc,const Object * argv)1403 Object scheme::transcoderCodecEx(VM* theVM, int argc, const Object* argv)
1404 {
1405     DeclareProcedureName("transcoder-codec");
1406     checkArgumentLength(1);
1407     argumentAsTranscoder(0, transcoder);
1408     return transcoder->codec();
1409 }
1410 
transcoderEolStyleEx(VM * theVM,int argc,const Object * argv)1411 Object scheme::transcoderEolStyleEx(VM* theVM, int argc, const Object* argv)
1412 {
1413     DeclareProcedureName("transcoder-eol-style");
1414     checkArgumentLength(1);
1415     argumentAsTranscoder(0, transcoder);
1416     return transcoder->eolStyleSymbol();
1417 }
1418 
transcoderErrorHandlingModeEx(VM * theVM,int argc,const Object * argv)1419 Object scheme::transcoderErrorHandlingModeEx(VM* theVM, int argc, const Object* argv)
1420 {
1421     DeclareProcedureName("transcoder-error-handling-mode");
1422     checkArgumentLength(1);
1423     argumentAsTranscoder(0, transcoder);
1424     return transcoder->errorHandlingModeSymbol();
1425 }
1426 
nullTerminatedBytevectorTostringEx(VM * theVM,int argc,const Object * argv)1427 Object scheme::nullTerminatedBytevectorTostringEx(VM* theVM, int argc, const Object* argv)
1428 {
1429     DeclareProcedureName("null-terminated-bytevector->string");
1430     checkArgumentLength(2);
1431 
1432     argumentAsByteVector(0, bytevector);
1433     argumentAsTranscoder(1, transcoder);
1434 
1435     int realLength = bytevector->length();
1436     int nullTerminatedLength = 0;
1437     for (int i = 0; i < realLength; i++, nullTerminatedLength++) {
1438         if (bytevector->u8Ref(i) == 0) {
1439             break;
1440         }
1441     }
1442 
1443     BinaryInputPort* port = new ByteArrayBinaryInputPort(bytevector->data(), nullTerminatedLength);
1444     TRY_WITHOUT_DSTR
1445         return Object::makeString(transcoder->getString(port));
1446     CATCH(ioError)
1447         ioError.arg1 = Object::makeBinaryInputPort(port);
1448         ioError.who = procedureName;
1449         ioError.irritants = Object::cons(argv[1], ioError.irritants);
1450         return callIOErrorAfter(theVM, ioError);
1451     END_TRY
1452 }
1453 
bytevectorTostringEx(VM * theVM,int argc,const Object * argv)1454 Object scheme::bytevectorTostringEx(VM* theVM, int argc, const Object* argv)
1455 {
1456     DeclareProcedureName("bytevector->string");
1457     checkArgumentLength(2);
1458 
1459     argumentAsByteVector(0, bytevector);
1460     argumentAsTranscoder(1, transcoder);
1461 
1462     BinaryInputPort* port = new ByteArrayBinaryInputPort(bytevector->data(), bytevector->length());
1463     TRY_WITHOUT_DSTR
1464         return Object::makeString(transcoder->getString(port));
1465     CATCH(ioError)
1466         ioError.arg1 = Object::makeBinaryInputPort(port);
1467         ioError.who = procedureName;
1468         ioError.irritants = Object::cons(argv[1], ioError.irritants);
1469         return callIOErrorAfter(theVM, ioError);
1470     END_TRY
1471 }
1472 
stringTobytevectorEx(VM * theVM,int argc,const Object * argv)1473 Object scheme::stringTobytevectorEx(VM* theVM, int argc, const Object* argv)
1474 {
1475     DeclareProcedureName("string->bytevector");
1476     checkArgumentLength(2);
1477     argumentAsString(0, text);
1478     argumentAsTranscoder(1, transcoder);
1479 
1480     ByteArrayBinaryOutputPort accum;
1481     TranscodedTextualOutputPort out(&accum, transcoder);
1482 
1483     for (ucs4string::const_iterator it = text->data().begin();
1484          it != text->data().end(); ++it) {
1485         TRY_WITHOUT_DSTR
1486             out.putChar(*it);
1487         CATCH(ioError)
1488             ioError.arg1 = Object::Nil;
1489             ioError.who = procedureName;
1490             return callIOErrorAfter(theVM, ioError);
1491         END_TRY
1492     }
1493     return Object::makeByteVector(accum.toByteVector());
1494 }
1495 
eofObjectEx(VM * theVM,int argc,const Object * argv)1496 Object scheme::eofObjectEx(VM* theVM, int argc, const Object* argv)
1497 {
1498     DeclareProcedureName("eof-object");
1499     checkArgumentLength(0);
1500     return Object::Eof;
1501 }
1502 
sysOpenBytevectorOutputPortEx(VM * theVM,int argc,const Object * argv)1503 Object scheme::sysOpenBytevectorOutputPortEx(VM* theVM, int argc, const Object* argv)
1504 {
1505     DeclareProcedureName("open-bytevector-output-port");
1506     checkArgumentLengthBetween(0, 1);
1507     if (0 == argc || argv[0].isFalse()) {
1508         return  Object::makeBinaryOutputPort(new ByteArrayBinaryOutputPort);
1509     } else {
1510         argumentAsTranscoder(0, transcoder);
1511         return Object::makeTextualOutputPort(new ByteArrayBinaryOutputPort(), transcoder);
1512     }
1513 }
1514 
sysGetBytevectorEx(VM * theVM,int argc,const Object * argv)1515 Object scheme::sysGetBytevectorEx(VM* theVM, int argc, const Object* argv)
1516 {
1517     DeclareProcedureName("get-bytevector");
1518     checkArgumentLength(1);
1519     const Object port = argv[0];
1520     if (port.isBinaryOutputPort()) {
1521         return Object::makeByteVector(reinterpret_cast<ByteArrayBinaryOutputPort*>(port.toBinaryOutputPort())->toByteVector());
1522     } else if (port.isTextualOutputPort()) {
1523         BinaryOutputPort* out = reinterpret_cast<TranscodedTextualOutputPort*>(port.toTextualOutputPort())->binaryPort();
1524         return Object::makeByteVector(reinterpret_cast<ByteArrayBinaryOutputPort*>(out)->toByteVector());
1525     } else {
1526         callAssertionViolationAfter(theVM, procedureName, "bytevector-port required", L1(argv[0]));
1527         return Object::Undef;
1528     }
1529 }
1530 
1531 /*
1532     file-options
1533 
1534     (file-options)
1535       If file exists:     raise &file-already-exists
1536       If does not exist:  create new file
1537     (file-options no-create)
1538       If file exists:     truncate
1539       If does not exist:  raise &file-does-not-exist
1540     (file-options no-fail)
1541       If file exists:     truncate
1542       If does not exist:  create new file
1543     (file-options no-truncate)
1544       If file exists:     raise &file-already-exists
1545       If does not exist:  create new file
1546     (file-options no-create no-fail)
1547       If file exists:     truncate
1548       If does not exist:  [N.B.] R6RS say nothing about this case, we choose raise &file-does-not-exist
1549     (file-options no-fail no-truncate)
1550       If file exists:     set port position to 0 (overwriting)
1551       If does not exist:  create new file
1552     (file-options no-create no-truncate)
1553       If file exists:     set port position to 0 (overwriting)
1554       If does not exist:  raise &file-does-not-exist
1555     (file-options no-create no-fail no-truncate)
1556       If file exists:     set port position to 0 (overwriting)
1557       If does not exist:  [N.B.] R6RS say nothing about this case, we choose raise &file-does-not-exist
1558 
1559 */
openFileOutputPortEx(VM * theVM,int argc,const Object * argv)1560 Object scheme::openFileOutputPortEx(VM* theVM, int argc, const Object* argv)
1561 {
1562     DeclareProcedureName("open-file-output-port");
1563     checkArgumentLengthBetween(1, 4);
1564     BinaryOutputPort* port = NULL;
1565     Transcoder* transcoder = NULL;
1566     int openFlags = 0;
1567 
1568     argumentAsString(0, path);
1569     const bool isFileExist = File::isExist(path->data());
1570     const bool isReadable = File::isReadable(path->data());
1571 
1572 
1573     if (argc == 1) {
1574         if (isFileExist) {
1575             return callIoFileAlreadyExistAfter(theVM, procedureName, "file already exists", argv[0]);
1576         }
1577         // default buffer mode is Block
1578         port = new BlockBufferedFileBinaryOutputPort(path->data(), openFlags);
1579     } else {
1580         argumentAsSimpleStruct(1, fileOptions);
1581 
1582         const bool emptyP = isEmpty(fileOptions);
1583         const bool noCreateP = isNoCreate(fileOptions);
1584         const bool noTruncateP = isNoTruncate(fileOptions);
1585         const bool noFailP = isNoFail(fileOptions);
1586 
1587         if (isFileExist && emptyP) {
1588             return callIoFileAlreadyExistAfter(theVM,  procedureName, "file already exists",argv[0]);
1589         } else if (noCreateP && noTruncateP) {
1590             if (!isFileExist) {
1591                 return callIoFileNotExistAfter(theVM, procedureName, "file-options no-create: file not exist", argv[0]);
1592             }
1593         } else if (noCreateP) {
1594             if (isFileExist) {
1595                 openFlags |= File::Truncate;
1596             } else {
1597                 return callIoFileNotExistAfter(theVM, procedureName, "file-options no-create: file not exist", argv[0]);
1598             }
1599         } else if (noFailP && noTruncateP) {
1600             if (!isFileExist) {
1601                 openFlags |= File::Truncate;
1602             }
1603         } else if (noFailP) {
1604             openFlags |= File::Truncate;
1605         } else if (noTruncateP) {
1606             if (isFileExist) {
1607                 return callIoFileAlreadyExistAfter(theVM, procedureName, "file-options no-trucate: file already exists", argv[0]);
1608             } else {
1609                 openFlags |= File::Truncate;
1610             }
1611         }
1612 
1613         if (argc == 2) {
1614             port = new BlockBufferedFileBinaryOutputPort(path->data(), openFlags);
1615         } else {
1616             argumentCheckSymbol(2, bufferMode);
1617 
1618             if (bufferMode == Symbol::BLOCK) {
1619                 port = new BlockBufferedFileBinaryOutputPort(path->data(), openFlags);
1620             } else if (bufferMode == Symbol::LINE) {
1621                 port = new LineBufferedFileBinaryOutputPort(path->data(), openFlags);
1622             } else if (bufferMode == Symbol::NONE) {
1623                 port = new FileBinaryOutputPort(path->data(), openFlags);
1624             } else {
1625                 callErrorAfter(theVM, procedureName, "invalid buffer-mode option", L1(argv[2]));
1626                 return Object::Undef;
1627             }
1628             if (argc == 4) {
1629                 argumentCheckTranscoderOrFalse(3, maybeTranscoder);
1630                 if (maybeTranscoder != Object::False) {
1631                     transcoder = maybeTranscoder.toTranscoder();
1632                 }
1633             }
1634         }
1635     }
1636 
1637     if ((port != NULL) && (MOSH_SUCCESS == port->open())) {
1638         if (transcoder == NULL) {
1639             const Object ret = Object::makeBinaryOutputPort(port);
1640             theVM->registerPort(ret);
1641             return ret;
1642         } else {
1643             const Object ret = Object::makeTextualOutputPort(port, transcoder);
1644             theVM->registerPort(ret);
1645             return ret;
1646         }
1647     } else {
1648         if (port->getFile() && port->getFile()->isLastErrorAcessError()) {
1649             if (isReadable) {
1650                 return callIoFileReadOnlyAfter(theVM, procedureName, port->getLastErrorMessage(), argv[0]);
1651             } else {
1652                 return callIoFileProtectionAfter(theVM, procedureName, port->getLastErrorMessage(), argv[0]);
1653             }
1654         } else {
1655             callErrorAfter(theVM, procedureName, port->getLastErrorMessage(), L1(argv[0]));
1656             return Object::Undef;
1657         }
1658     }
1659 }
1660 
openFileInputPortEx(VM * theVM,int argc,const Object * argv)1661 Object scheme::openFileInputPortEx(VM* theVM, int argc, const Object* argv)
1662 {
1663     DeclareProcedureName("open-file-input-port");
1664     checkArgumentLengthBetween(1, 4);
1665     BinaryInputPort* in = NULL;
1666     Transcoder* transcoder = NULL;
1667 
1668     // N.B. As R6RS says, we ignore "file-options" for input-port.
1669     if (argc == 1) {
1670         argumentAsString(0, path);
1671         // default buffer mode is Block
1672         in = new BufferedFileBinaryInputPort(path->data());
1673     } else if (argc == 2) {
1674         argumentAsString(0, path);
1675         argumentCheckSimpleStruct(1, fileOptions);
1676         // default buffer mode is Block
1677         in = new BufferedFileBinaryInputPort(path->data());
1678     } else if (argc == 3) {
1679         argumentAsString(0, path);
1680 
1681         argumentCheckSimpleStruct(1, fileOptions);
1682         argumentCheckSymbol(2, bufferMode);
1683 
1684         // N.B. On Mosh, buffer mode BLOCK == LINE.
1685         if (bufferMode == Symbol::BLOCK || bufferMode == Symbol::LINE) {
1686             in = new BufferedFileBinaryInputPort(path->data());
1687         } else if (bufferMode == Symbol::NONE) {
1688             in = new FileBinaryInputPort(path->data());
1689         } else {
1690             callErrorAfter(theVM, procedureName, "invalid buffer-mode option", L1(argv[2]));
1691             return Object::Undef;
1692         }
1693     } else if (argc == 4) {
1694         argumentAsString(0, path);
1695         argumentCheckSimpleStruct(1, fileOptions);
1696         argumentCheckSymbol(2, bufferMode);
1697         // N.B. On Mosh, buffer mode BLOCK == LINE.
1698         if (bufferMode == Symbol::BLOCK || bufferMode == Symbol::LINE) {
1699             in = new BufferedFileBinaryInputPort(path->data());
1700         } else if (bufferMode == Symbol::NONE) {
1701             in = new FileBinaryInputPort(path->data());
1702         } else {
1703             callErrorAfter(theVM, procedureName, "invalid buffer-mode option", L1(argv[2]));
1704             return Object::Undef;
1705         }
1706         argumentCheckTranscoderOrFalse(3, maybeTranscoder);
1707         if (maybeTranscoder != Object::False) {
1708             transcoder = maybeTranscoder.toTranscoder();
1709         }
1710     }
1711 
1712     if ((in != NULL) && (MOSH_SUCCESS == in->open())) {
1713         if (transcoder == NULL) {
1714             return Object::makeBinaryInputPort(in);
1715         } else {
1716             return Object::makeTextualInputPort(in, transcoder);
1717         }
1718     } else {
1719         if (in->getFile() && in->getFile()->isLastErrorAcessError()) {
1720             return callIoFileProtectionAfter(theVM, procedureName, in->getLastErrorMessage(), argv[0]);
1721         } else {
1722             callErrorAfter(theVM, procedureName, in->getLastErrorMessage(), L1(argv[0]));
1723             return Object::Undef;
1724         }
1725     }
1726 }
1727 
currentInputPortEx(VM * theVM,int argc,const Object * argv)1728 Object scheme::currentInputPortEx(VM* theVM, int argc, const Object* argv)
1729 {
1730     DeclareProcedureName("current-input-port");
1731     checkArgumentLength(0);
1732     return theVM->currentInputPort();
1733 }
1734 
currentOutputPortEx(VM * theVM,int argc,const Object * argv)1735 Object scheme::currentOutputPortEx(VM* theVM, int argc, const Object* argv)
1736 {
1737     DeclareProcedureName("current-output-port");
1738     checkArgumentLength(0);
1739     return theVM->currentOutputPort();
1740 }
1741 
setCurrentInputPortDEx(VM * theVM,int argc,const Object * argv)1742 Object scheme::setCurrentInputPortDEx(VM* theVM, int argc, const Object* argv)
1743 {
1744     DeclareProcedureName("set-current-input-port!");
1745     checkArgumentLength(1);
1746 
1747     argumentCheckTextualInputPort(0, textualInputPort);
1748     theVM->setCurrentInputPort(textualInputPort);
1749     return Object::UnBound;
1750 }
1751 
setCurrentOutputPortDEx(VM * theVM,int argc,const Object * argv)1752 Object scheme::setCurrentOutputPortDEx(VM* theVM, int argc, const Object* argv)
1753 {
1754     DeclareProcedureName("set-current-output-port!");
1755     checkArgumentLength(1);
1756 
1757     argumentCheckTextualOutputPort(0, textualOutputPort);
1758     theVM->setCurrentOutputPort(textualOutputPort);
1759     return Object::Undef;
1760 }
1761 
standardInputPortEx(VM * theVM,int argc,const Object * argv)1762 Object scheme::standardInputPortEx(VM* theVM, int argc, const Object* argv)
1763 {
1764     static const Object port = Object::makeBinaryInputPort(new StandardInputPort());
1765     DeclareProcedureName("standard-input-port");
1766     checkArgumentLength(0);
1767     return port;
1768 }
1769 
standardOutputPortEx(VM * theVM,int argc,const Object * argv)1770 Object scheme::standardOutputPortEx(VM* theVM, int argc, const Object* argv)
1771 {
1772     static const Object port = Object::makeBinaryOutputPort(new StandardOutputPort());
1773     DeclareProcedureName("standard-output-port");
1774     checkArgumentLength(0);
1775     return port;
1776 }
1777 
standardErrorPortEx(VM * theVM,int argc,const Object * argv)1778 Object scheme::standardErrorPortEx(VM* theVM, int argc, const Object* argv)
1779 {
1780     static const Object port = Object::makeBinaryOutputPort(new StandardErrorPort());
1781     DeclareProcedureName("standard-error-port");
1782     checkArgumentLength(0);
1783     return port;
1784 }
1785 
1786 
directoryListEx(VM * theVM,int argc,const Object * argv)1787 Object scheme::directoryListEx(VM* theVM, int argc, const Object* argv)
1788 {
1789     DeclareProcedureName("directory-list");
1790     checkArgumentLength(1);
1791     argumentAsString(0, path);
1792     const Object directories = readDirectory(path->data());
1793     if (directories.isFalse()) {
1794         callAssertionViolationAfter(theVM, procedureName, "could't open dir", L1(argv[0]));
1795         return Object::Undef;
1796     }
1797     return directories;
1798 }
1799 
bufferModePEx(VM * theVM,int argc,const Object * argv)1800 Object scheme::bufferModePEx(VM* theVM, int argc, const Object* argv)
1801 {
1802     DeclareProcedureName("buffer-mode?");
1803     checkArgumentLength(1);
1804     argumentCheckSymbol(0, bufferMode);
1805 
1806     if (bufferMode == Symbol::NONE ||
1807         bufferMode == Symbol::LINE ||
1808         bufferMode == Symbol::BLOCK) {
1809         return Object::True;
1810     }
1811 
1812     return Object::False;
1813 }
1814 
inputPortPEx(VM * theVM,int argc,const Object * argv)1815 Object scheme::inputPortPEx(VM* theVM, int argc, const Object* argv)
1816 {
1817     DeclareProcedureName("input-port?");
1818     checkArgumentLength(1);
1819     return Object::makeBool(argv[0].isInputPort());
1820 }
1821 
binaryPortPEx(VM * theVM,int argc,const Object * argv)1822 Object scheme::binaryPortPEx(VM* theVM, int argc, const Object* argv)
1823 {
1824     DeclareProcedureName("binary-port?");
1825     checkArgumentLength(1);
1826     return Object::makeBool(argv[0].isBinaryPort());
1827 }
1828 
textualPortPEx(VM * theVM,int argc,const Object * argv)1829 Object scheme::textualPortPEx(VM* theVM, int argc, const Object* argv)
1830 {
1831     DeclareProcedureName("textual-port?");
1832     checkArgumentLength(1);
1833     return Object::makeBool(argv[0].isTextualPort());
1834 }
1835 
portPEx(VM * theVM,int argc,const Object * argv)1836 Object scheme::portPEx(VM* theVM, int argc, const Object* argv)
1837 {
1838     DeclareProcedureName("port?");
1839     checkArgumentLength(1);
1840     return Object::makeBool(argv[0].isPort());
1841 }
1842 
portTranscoderEx(VM * theVM,int argc,const Object * argv)1843 Object scheme::portTranscoderEx(VM* theVM, int argc, const Object* argv)
1844 {
1845     DeclareProcedureName("port-transcoder");
1846     checkArgumentLength(1);
1847     argumentCheckPort(0, port);
1848 
1849     if (port.isTextualOutputPort()) {
1850         return Object::makeTranscoder(port.toTextualOutputPort()->transcoder());
1851     } else if (port.isTextualInputPort()) {
1852         return Object::makeTranscoder(port.toTextualInputPort()->transcoder());
1853     } else {
1854         return Object::False;
1855     }
1856 }
1857 
1858 
putU8Ex(VM * theVM,int argc,const Object * argv)1859 Object scheme::putU8Ex(VM* theVM, int argc, const Object* argv)
1860 {
1861     DeclareProcedureName("put-u8");
1862     checkArgumentLength(2);
1863     argumentAsBinaryOutputPort(0, binaryOutputPort);
1864     argumentAsOctet(1, octet);
1865 
1866     binaryOutputPort->putU8(octet);
1867 
1868     return Object::Undef;
1869 }
1870 
putStringEx(VM * theVM,int argc,const Object * argv)1871 Object scheme::putStringEx(VM* theVM, int argc, const Object* argv)
1872 {
1873     DeclareProcedureName("put-string");
1874     checkArgumentLengthBetween(2, 4);
1875     argumentAsTextualOutputPort(0, textualOutputPort);
1876     argumentAsString(1, stringObj);
1877     const ucs4string string = stringObj->data();
1878     if (argc < 3) {
1879         TRY_WITHOUT_DSTR
1880             textualOutputPort->putString(string);
1881         CATCH(IOError)
1882             ioError.arg1 = argv[0];
1883             ioError.who = procedureName;
1884             return callIOErrorAfter(theVM, ioError);
1885         END_TRY
1886         return Object::Undef;
1887     }
1888 
1889     argumentCheckExactInteger(2, startObj);
1890     int start;
1891     if (startObj.isFixnum()) {
1892         start = startObj.toFixnum();
1893     } else { // startObj.isBignum()
1894         start = startObj.toBignum()->toS32();
1895     }
1896     if (argc < 4) {
1897         TRY_WITHOUT_DSTR
1898             textualOutputPort->putString(string.substr(start, string.length()-start));
1899         CATCH(IOError)
1900             ioError.arg1 = argv[0];
1901             ioError.who = procedureName;
1902             return callIOErrorAfter(theVM, ioError);
1903         END_TRY
1904         return Object::Undef;
1905     }
1906 
1907     argumentCheckExactInteger(3, countObj);
1908     int count;
1909     if (countObj.isFixnum()) {
1910         count = countObj.toFixnum();
1911     } else { // countObj.isBignum()
1912         count = countObj.toBignum()->toS32();
1913     }
1914     TRY_WITHOUT_DSTR
1915         textualOutputPort->putString(string.substr(start, count));
1916     CATCH(IOError)
1917         ioError.arg1 = argv[0];
1918         ioError.who = procedureName;
1919         return callIOErrorAfter(theVM, ioError);
1920     END_TRY
1921     return Object::Undef;
1922 }
1923 
flushOutputPortEx(VM * theVM,int argc,const Object * argv)1924 Object scheme::flushOutputPortEx(VM* theVM, int argc, const Object* argv)
1925 {
1926     DeclareProcedureName("flush-output-port");
1927     checkArgumentLength(1);
1928     TRY_WITHOUT_DSTR
1929         const Object outputPort = argv[0];
1930         if (outputPort.isBinaryOutputPort()) {
1931             outputPort.toBinaryOutputPort()->flush();
1932         } else if (outputPort.isBinaryInputOutputPort()) {
1933             outputPort.toBinaryInputOutputPort()->flush();
1934         } else if (outputPort.isTextualOutputPort()) {
1935             outputPort.toTextualOutputPort()->flush();
1936         } else if (outputPort.isTextualInputOutputPort()) {
1937             outputPort.toTextualInputOutputPort()->flush();
1938         } else {
1939             callAssertionViolationAfter(theVM, procedureName, "output-port required", L1(outputPort));
1940         }
1941         return Object::Undef;
1942     CATCH(ioError)
1943         ioError.arg1 = argv[0];
1944         ioError.who = procedureName;
1945         return callIOErrorAfter(theVM, ioError);
1946     END_TRY
1947 }
1948 
outputPortBufferModeEx(VM * theVM,int argc,const Object * argv)1949 Object scheme::outputPortBufferModeEx(VM* theVM, int argc, const Object* argv)
1950 {
1951     DeclareProcedureName("output-port-buffer-mode");
1952     checkArgumentLength(1);
1953 
1954     argumentCheckOutputPort(0, outputPort);
1955     enum OutputPort::bufferMode bufferMode;
1956     if (outputPort.isBinaryPort()) {
1957         bufferMode = outputPort.toBinaryOutputPort()->bufferMode();
1958     } else if (outputPort.isTextualPort()) {
1959         bufferMode = outputPort.toTextualOutputPort()->bufferMode();
1960     } else {
1961         callAssertionViolationAfter(theVM, procedureName, "output-port required", L1(outputPort));
1962         return Object::Undef;
1963     }
1964 
1965     if (bufferMode == OutputPort::BLOCK) {
1966         return Symbol::BLOCK;
1967     } else if (bufferMode == OutputPort::LINE) {
1968         return Symbol::LINE;
1969     } else {
1970         return Symbol::NONE;
1971     }
1972 }
1973 
fileStatCtimeEx(VM * theVM,int argc,const Object * argv)1974 Object scheme::fileStatCtimeEx(VM* theVM, int argc, const Object* argv)
1975 {
1976     DeclareProcedureName("file-stat-ctime");
1977     checkArgumentLength(1);
1978     argumentAsString(0, path);
1979     const Object tm = File::changeTime(path->data());
1980     if (tm.isUndef()) {
1981         callAssertionViolationAfter(theVM, procedureName, getLastErrorMessage(), L1(argv[0]));
1982         return Object::Undef;
1983     } else {
1984         return tm;
1985     }
1986 }
1987 
fileStatAtimeEx(VM * theVM,int argc,const Object * argv)1988 Object scheme::fileStatAtimeEx(VM* theVM, int argc, const Object* argv)
1989 {
1990     DeclareProcedureName("file-stat-atime");
1991     checkArgumentLength(1);
1992     argumentAsString(0, path);
1993     const Object tm = File::accessTime(path->data());
1994     if (tm.isUndef()) {
1995         callAssertionViolationAfter(theVM, procedureName, getLastErrorMessage(), L1(argv[0]));
1996         return Object::Undef;
1997     } else {
1998         return tm;
1999     }
2000 }
2001 
fileStatMtimeEx(VM * theVM,int argc,const Object * argv)2002 Object scheme::fileStatMtimeEx(VM* theVM, int argc, const Object* argv)
2003 {
2004     DeclareProcedureName("file-stat-mtime");
2005     checkArgumentLength(1);
2006     argumentAsString(0, path);
2007     const Object tm = File::modifyTime(path->data());
2008     if (tm.isUndef()) {
2009         callAssertionViolationAfter(theVM, procedureName, getLastErrorMessage(), L1(argv[0]));
2010         return Object::Undef;
2011     } else {
2012         return tm;
2013     }
2014 }
2015 
fileExecutablePEx(VM * theVM,int argc,const Object * argv)2016 Object scheme::fileExecutablePEx(VM* theVM, int argc, const Object* argv)
2017 {
2018     DeclareProcedureName("file-executable?");
2019     checkArgumentLength(1);
2020     argumentAsString(0, path);
2021     return Object::makeBool(File::isExecutable(path->data()));
2022 }
2023 
fileWritablePEx(VM * theVM,int argc,const Object * argv)2024 Object scheme::fileWritablePEx(VM* theVM, int argc, const Object* argv)
2025 {
2026     DeclareProcedureName("file-writable?");
2027     checkArgumentLength(1);
2028     argumentAsString(0, path);
2029     return Object::makeBool(File::isWritable(path->data()));
2030 }
2031 
fileReadablePEx(VM * theVM,int argc,const Object * argv)2032 Object scheme::fileReadablePEx(VM* theVM, int argc, const Object* argv)
2033 {
2034     DeclareProcedureName("file-readable?");
2035     checkArgumentLength(1);
2036     argumentAsString(0, path);
2037     return Object::makeBool(File::isReadable(path->data()));
2038 }
2039 
fileRegularPEx(VM * theVM,int argc,const Object * argv)2040 Object scheme::fileRegularPEx(VM* theVM, int argc, const Object* argv)
2041 {
2042     DeclareProcedureName("file-regular?");
2043     checkArgumentLength(1);
2044     argumentAsString(0, path);
2045     return Object::makeBool(File::isRegular(path->data()));
2046 }
2047 
fileDirectoryPEx(VM * theVM,int argc,const Object * argv)2048 Object scheme::fileDirectoryPEx(VM* theVM, int argc, const Object* argv)
2049 {
2050     DeclareProcedureName("file-directory?");
2051     checkArgumentLength(1);
2052     argumentAsString(0, path);
2053     return Object::makeBool(isDirectory(path->data()));
2054 }
2055 
fileSymbolicLinkPEx(VM * theVM,int argc,const Object * argv)2056 Object scheme::fileSymbolicLinkPEx(VM* theVM, int argc, const Object* argv)
2057 {
2058     DeclareProcedureName("file-symbolic-link?");
2059     checkArgumentLength(1);
2060     argumentAsString(0, path);
2061     return Object::makeBool(File::isSymbolicLink(path->data()));
2062 }
2063 
fileSizeInBytesEx(VM * theVM,int argc,const Object * argv)2064 Object scheme::fileSizeInBytesEx(VM* theVM, int argc, const Object* argv)
2065 {
2066     DeclareProcedureName("file-size-in-bytes");
2067     checkArgumentLength(1);
2068     argumentAsString(0, path);
2069     const Object size = File::size(path->data());
2070     if (size.isUndef()) {
2071         callAssertionViolationAfter(theVM, procedureName, getLastErrorMessage(), L1(argv[0]));
2072         return Object::Undef;
2073     } else {
2074         return size;
2075     }
2076 }
2077 
createSymbolicLinkEx(VM * theVM,int argc,const Object * argv)2078 Object scheme::createSymbolicLinkEx(VM* theVM, int argc, const Object* argv)
2079 {
2080     DeclareProcedureName("create-symbolic-link");
2081     checkArgumentLength(2);
2082     argumentAsString(0, oldPath);
2083     argumentAsString(1, newPath);
2084     if (File::createSymbolicLink(oldPath->data(), newPath->data())) {
2085         return Object::Undef;
2086     } else {
2087         callAssertionViolationAfter(theVM, procedureName, getLastErrorMessage(), L2(argv[0], argv[1]));
2088         return Object::Undef;
2089     }
2090 }
2091 
renameFileEx(VM * theVM,int argc,const Object * argv)2092 Object scheme::renameFileEx(VM* theVM, int argc, const Object* argv)
2093 {
2094     DeclareProcedureName("rename-file");
2095     checkArgumentLength(2);
2096     argumentAsString(0, oldPath);
2097     argumentAsString(1, newPath);
2098     if (File::rename(oldPath->data(), newPath->data())) {
2099         return Object::Undef;
2100     } else {
2101         callAssertionViolationAfter(theVM, procedureName, getLastErrorMessage(), L2(argv[0], argv[1]));
2102         return Object::Undef;
2103     }
2104 }
2105 
deleteDirectoryEx(VM * theVM,int argc,const Object * argv)2106 Object scheme::deleteDirectoryEx(VM* theVM, int argc, const Object* argv)
2107 {
2108     DeclareProcedureName("delete-directory");
2109     checkArgumentLength(1);
2110     argumentAsString(0, path);
2111     if (File::deleteFileOrDirectory(path->data())) {
2112         return Object::Undef;
2113     } else {
2114         callAssertionViolationAfter(theVM, procedureName, getLastErrorMessage(), L1(argv[0]));
2115         return Object::Undef;
2116     }
2117 }
2118 
createDirectoryEx(VM * theVM,int argc,const Object * argv)2119 Object scheme::createDirectoryEx(VM* theVM, int argc, const Object* argv)
2120 {
2121     DeclareProcedureName("create-directory");
2122     checkArgumentLength(1);
2123     argumentAsString(0, path);
2124     if (createDirectory(path->data())) {
2125         return Object::Undef;
2126     } else {
2127         callAssertionViolationAfter(theVM, procedureName, getLastErrorMessage(), L1(argv[0]));
2128         return Object::Undef;
2129     }
2130 }
2131