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