PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/iovm/source/IoFile.c

http://github.com/stevedekorte/io
C | 1255 lines | 797 code | 231 blank | 227 comment | 96 complexity | 7232c67a4c252d887a91348418a78b07 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. //metadoc File copyright Steve Dekorte 2002
  2. //metadoc File license BSD revised
  3. //metadoc File category Core
  4. /*metadoc File description
  5. Encapsulates file i/o. Here's an example of opening a file,
  6. and reversing its lines:
  7. <pre>
  8. file := File clone openForUpdating("/tmp/test")
  9. lines := file readLines reverse
  10. file rewind
  11. lines foreach(line, file write(line, "\n"))
  12. file close
  13. </pre>
  14. */
  15. #include "IoDate.h"
  16. #include "IoFile.h"
  17. #include "IoFile_stat.h"
  18. #include "IoSeq.h"
  19. #include "IoState.h"
  20. #include "IoCFunction.h"
  21. #include "IoObject.h"
  22. #include "IoList.h"
  23. #include "IoSeq.h"
  24. #include "UArray.h"
  25. #include "PortableTruncate.h"
  26. #include <errno.h>
  27. #include <stdio.h>
  28. #include <sys/types.h>
  29. #include <sys/stat.h>
  30. /*#include <sys/wait.h>*/
  31. #if !defined(_MSC_VER) && !defined(__SYMBIAN32__)
  32. #include <unistd.h> /* ok, this isn't ANSI */
  33. #endif
  34. #if defined(_MSC_VER) && !defined(__SYMBIAN32__)
  35. #include <direct.h>
  36. #define getcwd _getcwd
  37. #define popen(x,y) _popen(x,y)
  38. #define pclose(x) _pclose(x)
  39. #endif
  40. #if defined(__SYMBIAN32__)
  41. static int pclose(void* f) { return 0; }
  42. static int popen(void* f, int m) { return 0; }
  43. static int rename(void* a, void* b) { return 0; }
  44. static char* getcwd(char* buf, int size) { return 0; }
  45. #endif
  46. static const char *protoId = "File";
  47. #define DATA(self) ((IoFileData *)IoObject_dataPointer(self))
  48. int fileExists(char *path);
  49. IoTag *IoFile_newTag(void *state)
  50. {
  51. IoTag *tag = IoTag_newWithName_("File");
  52. IoTag_state_(tag, state);
  53. IoTag_cloneFunc_(tag, (IoTagCloneFunc *)IoFile_rawClone);
  54. IoTag_markFunc_(tag, (IoTagMarkFunc *)IoFile_mark);
  55. IoTag_freeFunc_(tag, (IoTagFreeFunc *)IoFile_free);
  56. //IoTag_writeToStreamFunc_(tag, (IoTagWriteToStreamFunc *)IoFile_writeToStream_);
  57. //IoTag_readFromStreamFunc_(tag, (IoTagReadFromStreamFunc *)IoFile_readFromStream_);
  58. return tag;
  59. }
  60. IoFile *IoFile_proto(void *state)
  61. {
  62. IoMethodTable methodTable[] = {
  63. {"descriptor", IoFile_descriptor},
  64. {"descriptorId", IoFile_descriptor}, // compatible with Socket
  65. // standard I/O
  66. {"standardInput", IoFile_standardInput},
  67. {"standardOutput", IoFile_standardOutput},
  68. {"standardError", IoFile_standardError},
  69. // path
  70. {"setPath", IoFile_setPath},
  71. {"path", IoFile_path},
  72. {"name", IoFile_lastPathComponent},
  73. {"temporaryFile", IoFile_temporaryFile},
  74. // info
  75. {"exists", IoFile_exists},
  76. {"size", IoFile_size},
  77. // open and close
  78. {"openForReading", IoFile_openForReading},
  79. {"openForUpdating", IoFile_openForUpdating},
  80. {"openForAppending", IoFile_openForAppending},
  81. {"mode", IoFile_mode},
  82. {"open", IoFile_open},
  83. {"reopen", IoFile_reopen},
  84. {"popen", IoFile_popen},
  85. {"close", IoFile_close},
  86. {"isOpen", IoFile_isOpen},
  87. // reading
  88. {"contents", IoFile_contents},
  89. {"asBuffer", IoFile_asBuffer},
  90. {"readLine", IoFile_readLine},
  91. {"readLines", IoFile_readLines},
  92. {"readStringOfLength", IoFile_readStringOfLength_},
  93. {"readBufferOfLength", IoFile_readBufferOfLength_},
  94. {"readToBufferLength", IoFile_readToBufferLength},
  95. {"at", IoFile_at},
  96. {"foreach", IoFile_foreach},
  97. {"foreachLine", IoFile_foreachLine},
  98. // writing
  99. {"write", IoFile_write},
  100. {"atPut", IoFile_atPut},
  101. {"flush", IoFile_flush},
  102. // positioning
  103. {"rewind", IoFile_rewind},
  104. {"setPosition", IoFile_position_},
  105. {"position", IoFile_position},
  106. {"positionAtEnd", IoFile_positionAtEnd},
  107. {"isAtEnd", IoFile_isAtEnd},
  108. // other
  109. {"remove", IoFile_remove},
  110. {"moveTo", IoFile_moveTo_},
  111. {"truncateToSize", IoFile_truncateToSize},
  112. /*
  113. {"makeUnbuffered", IoFile_makeUnbuffered},
  114. {"makeLineBuffered", IoFile_makeLineBuffered},
  115. {"makeFullyBuffered", IoFile_makeFullyBuffered},
  116. */
  117. {NULL, NULL},
  118. };
  119. IoObject *self = IoObject_new(state);
  120. IoObject_tag_(self, IoFile_newTag(state));
  121. IoObject_setDataPointer_(self, io_calloc(1, sizeof(IoFileData)));
  122. DATA(self)->path = IOSYMBOL("");
  123. DATA(self)->mode = IOSYMBOL("r+");
  124. DATA(self)->flags = IOFILE_FLAGS_NONE;
  125. IoState_registerProtoWithId_((IoState *)state, self, protoId);
  126. IoObject_addMethodTable_(self, methodTable);
  127. IoFile_statInit(self);
  128. return self;
  129. }
  130. IoFile *IoFile_rawClone(IoFile *proto)
  131. {
  132. IoObject *self = IoObject_rawClonePrimitive(proto);
  133. IoObject_setDataPointer_(self, cpalloc(IoObject_dataPointer(proto), sizeof(IoFileData)));
  134. DATA(self)->info = NULL;
  135. DATA(self)->stream = (FILE *)NULL;
  136. DATA(self)->flags = IOFILE_FLAGS_NONE;
  137. return self;
  138. }
  139. IoFile *IoFile_new(void *state)
  140. {
  141. IoObject *proto = IoState_protoWithId_((IoState *)state, protoId);
  142. return IOCLONE(proto);
  143. }
  144. IoFile *IoFile_newWithPath_(void *state, IoSymbol *path)
  145. {
  146. IoFile *self = IoFile_new(state);
  147. DATA(self)->path = IOREF(path);
  148. return self;
  149. }
  150. IoFile *IoFile_newWithStream_(void *state, FILE *stream)
  151. {
  152. IoFile *self = IoFile_new(state);
  153. DATA(self)->stream = stream;
  154. return self;
  155. }
  156. IoFile *IoFile_cloneWithPath_(IoFile *self, IoSymbol *path)
  157. {
  158. IoFile *f = IOCLONE(self);
  159. DATA(f)->path = IOREF(path);
  160. return f;
  161. }
  162. void IoFile_mark(IoFile *self)
  163. {
  164. IoObject_shouldMarkIfNonNull(DATA(self)->path);
  165. IoObject_shouldMarkIfNonNull(DATA(self)->mode);
  166. }
  167. void IoFile_free(IoFile *self)
  168. {
  169. if (NULL == IoObject_dataPointer(self))
  170. {
  171. return;
  172. }
  173. IoFile_justClose(self);
  174. if (DATA(self)->info)
  175. {
  176. io_free(DATA(self)->info);
  177. }
  178. io_free(IoObject_dataPointer(self));
  179. }
  180. void IoFile_writeToStream_(IoFile *self, BStream *stream)
  181. {
  182. BStream_writeTaggedUArray_(stream, IoSeq_rawUArray(DATA(self)->path));
  183. BStream_writeTaggedUArray_(stream, IoSeq_rawUArray(DATA(self)->mode));
  184. }
  185. void *IoFile_readFromStream_(IoFile *self, BStream *stream)
  186. {
  187. IoSymbol *mode;
  188. IoSymbol *path = IoState_symbolWithUArray_copy_(IOSTATE, BStream_readTaggedUArray(stream), 1);
  189. DATA(self)->path = IOREF(path);
  190. mode = IoState_symbolWithUArray_copy_(IOSTATE, BStream_readTaggedUArray(stream), 1);
  191. DATA(self)->mode = IOREF(mode);
  192. return self;
  193. }
  194. void IoFile_justClose(IoFile *self)
  195. {
  196. FILE *stream = DATA(self)->stream;
  197. if (stream)
  198. {
  199. if (stream != stdout && stream != stdin)
  200. {
  201. if (DATA(self)->flags == IOFILE_FLAGS_PIPE)
  202. {
  203. int exitStatus = pclose(stream);
  204. #if !defined(_MSC_VER) && !defined(__MINGW32__) /* No sys/wait.h in mingw, therefore can't use WIFEXITED, WEXITSTATUS, etc. */
  205. if(WIFEXITED(exitStatus) == 1)
  206. {
  207. exitStatus = WEXITSTATUS(exitStatus);
  208. IoObject_setSlot_to_(self, IOSYMBOL("exitStatus"),
  209. IONUMBER(exitStatus));
  210. }
  211. else if(WIFSIGNALED(exitStatus) == 1) {
  212. exitStatus = WTERMSIG(exitStatus);
  213. IoObject_setSlot_to_(self, IOSYMBOL("termSignal"),
  214. IONUMBER(exitStatus));
  215. }
  216. else
  217. {
  218. printf("Did not exit normally. Returned %d (%d)\n", exitStatus, WEXITSTATUS(exitStatus));
  219. }
  220. #else
  221. IoObject_setSlot_to_(self, IOSYMBOL("exitStatus"),
  222. IONUMBER(exitStatus));
  223. #endif
  224. }
  225. else
  226. {
  227. fclose(stream);
  228. DATA(self)->flags = IOFILE_FLAGS_NONE;
  229. }
  230. }
  231. DATA(self)->stream = (FILE *)NULL;
  232. }
  233. }
  234. int fileExists(char *path)
  235. {
  236. struct stat statInfo;
  237. return stat(path, &statInfo) == 0;
  238. }
  239. int IoFile_justExists(IoFile *self)
  240. {
  241. return fileExists(UTF8CSTRING(DATA(self)->path));
  242. }
  243. int IoFile_create(IoFile *self)
  244. {
  245. FILE *fp = fopen(UTF8CSTRING(DATA(self)->path), "w");
  246. if (fp)
  247. {
  248. fclose(fp);
  249. return 1;
  250. }
  251. return 0;
  252. }
  253. /* ----------------------------------------------------------- */
  254. IO_METHOD(IoFile, descriptor)
  255. {
  256. /*doc File descriptor
  257. Returns the file's descriptor as a number.
  258. */
  259. if (DATA(self)->stream)
  260. {
  261. return IONUMBER(fileno(DATA(self)->stream));
  262. }
  263. else
  264. {
  265. return IONIL(self);
  266. }
  267. }
  268. IO_METHOD(IoFile, standardInput)
  269. {
  270. /*doc File standardInput
  271. Returns a new File whose stream is set to the standard input stream.
  272. */
  273. IoFile *newFile = IoFile_new(IOSTATE);
  274. DATA(newFile)->path = IOREF(IOSYMBOL("<standard input>"));
  275. DATA(newFile)->mode = IOREF(IOSYMBOL("r"));
  276. DATA(newFile)->stream = stdin;
  277. DATA(newFile)->flags = IOFILE_FLAGS_NONE;
  278. return newFile;
  279. }
  280. IO_METHOD(IoFile, standardOutput)
  281. {
  282. /*doc File standardOutput
  283. Returns a new File whose stream is set to the standard output stream.
  284. */
  285. IoFile *newFile = IoFile_new(IOSTATE);
  286. DATA(newFile)->path = IOREF(IOSYMBOL("<standard output>"));
  287. DATA(newFile)->mode = IOREF(IOSYMBOL("w"));
  288. DATA(newFile)->stream = stdout;
  289. DATA(newFile)->flags = IOFILE_FLAGS_NONE;
  290. return newFile;
  291. }
  292. IO_METHOD(IoFile, standardError)
  293. {
  294. /*doc File standardError
  295. Returns a new File whose stream is set to the standard error stream.
  296. */
  297. IoFile *newFile = IoFile_new(IOSTATE);
  298. DATA(newFile)->path = IOREF(IOSYMBOL("<standard error>"));
  299. DATA(newFile)->mode = IOREF(IOSYMBOL("w"));
  300. DATA(newFile)->stream = stderr;
  301. DATA(newFile)->flags = IOFILE_FLAGS_NONE;
  302. return newFile;
  303. }
  304. IO_METHOD(IoFile, setPath)
  305. {
  306. /*doc File setPath(aString)
  307. Sets the file path of the receiver to pathString.
  308. The default path is an empty string. Returns self.
  309. */
  310. DATA(self)->path = IOREF(IoMessage_locals_symbolArgAt_(m, locals, 0));
  311. return self;
  312. }
  313. IO_METHOD(IoFile, path)
  314. {
  315. /*doc File path
  316. Returns the file path of the receiver.
  317. */
  318. return DATA(self)->path;
  319. }
  320. IO_METHOD(IoFile, lastPathComponent)
  321. {
  322. /*doc File name
  323. Returns the last path component of the file path.
  324. */
  325. return IoSeq_lastPathComponent(DATA(self)->path, locals, m);
  326. }
  327. IO_METHOD(IoFile, mode)
  328. {
  329. /*doc File mode
  330. Returns the open mode of the file(either read, update or append).
  331. */
  332. char *mode = IoSeq_asCString(DATA(self)->mode);
  333. if (!strcmp(mode, "r")) { return IOSYMBOL("read"); }
  334. if (!strcmp(mode, "r+")) { return IOSYMBOL("update"); }
  335. if (!strcmp(mode, "a+")) { return IOSYMBOL("append"); }
  336. return IONIL(self);
  337. }
  338. IO_METHOD(IoFile, temporaryFile)
  339. {
  340. /*doc File temporaryFile
  341. Returns a new File object with an open temporary file. The file is
  342. automatically deleted when the returned File object is closed or garbage collected.
  343. */
  344. IoFile *newFile = IoFile_new(IOSTATE);
  345. DATA(newFile)->stream = tmpfile();
  346. return newFile;
  347. }
  348. IO_METHOD(IoFile, openForReading)
  349. {
  350. /*doc File openForReading(optionalPathString)
  351. Sets the file mode to read (reading only) and calls open(optionalPathString).
  352. */
  353. DATA(self)->mode = IOREF(IOSYMBOL("r"));
  354. return IoFile_open(self, locals, m);
  355. }
  356. IO_METHOD(IoFile, openForUpdating)
  357. {
  358. /*doc File openForUpdating(optionalPathString)
  359. Sets the file mode to update (reading and writing) and calls
  360. open(optionalPathString). This will not delete the file if it already exists.
  361. Use the remove method first if you need to delete an existing file before opening a new one.
  362. */
  363. DATA(self)->mode = IOREF(IOSYMBOL("r+"));
  364. return IoFile_open(self, locals, m);
  365. }
  366. IO_METHOD(IoFile, openForAppending)
  367. {
  368. /*doc File openForAppending(optionalPathString)
  369. Sets the file mode to append (writing to the end of the file)
  370. and calls open(optionalPathString).
  371. */
  372. DATA(self)->mode = IOREF(IOSYMBOL("a+"));
  373. return IoFile_open(self, locals, m);
  374. }
  375. IO_METHOD(IoFile, open)
  376. {
  377. /*doc File open(optionalPathString)
  378. Opens the file. Creates one if it does not exist.
  379. If the optionalPathString argument is provided, the path is set to it before
  380. opening. Returns self or raises an File exception on error.
  381. */
  382. char *mode = CSTRING(DATA(self)->mode);
  383. DATA(self)->flags = IOFILE_FLAGS_NONE;
  384. if (IoMessage_argCount(m) > 0)
  385. {
  386. DATA(self)->path = IOREF(IoMessage_locals_symbolArgAt_(m, locals, 0));
  387. }
  388. if (!DATA(self)->stream)
  389. {
  390. if (!IoFile_justExists(self) && strcmp(mode, "r") != 0 )
  391. {
  392. IoFile_create(self);
  393. if(!IoFile_justExists(self))
  394. {
  395. IoState_error_(IOSTATE, m, "unable to create file '%s': %s", UTF8CSTRING(DATA(self)->path), strerror(errno));
  396. }
  397. }
  398. DATA(self)->stream = fopen(UTF8CSTRING(DATA(self)->path), mode);
  399. }
  400. if (DATA(self)->stream == NULL)
  401. {
  402. IoState_error_(IOSTATE, m, "unable to open file path '%s': %s", UTF8CSTRING(DATA(self)->path), strerror(errno));
  403. }
  404. return self;
  405. }
  406. IO_METHOD(IoFile, reopen)
  407. {
  408. /*doc File reopen(otherFile, mode)
  409. Reopens otherFile and redirects its stream to this file's path using mode.
  410. If mode is omitted, it is copied from otherFile.
  411. Returns self or raises a File exception on error.
  412. */
  413. IoFile *otherFile;
  414. IoSeq *mode;
  415. DATA(self)->flags = IOFILE_FLAGS_NONE;
  416. IoMessage_assertArgCount_receiver_(m, 1, self);
  417. otherFile = IoMessage_locals_valueArgAt_(m, locals, 0);
  418. IOASSERT(ISFILE(otherFile), "arg must be a File");
  419. mode = IoMessage_locals_valueArgAt_(m, locals, 1);
  420. if(ISSEQ(mode))
  421. {
  422. DATA(self)->mode = IOREF(mode);
  423. }
  424. else
  425. {
  426. DATA(self)->mode = IOREF(IoSeq_newWithUArray_copy_(IOSTATE, (UArray *)DATA(DATA(otherFile)->mode), 1));
  427. }
  428. if (!DATA(self)->stream)
  429. {
  430. FILE *fp = freopen(UTF8CSTRING(DATA(self)->path), CSTRING(DATA(self)->mode), DATA(otherFile)->stream);
  431. if (fp)
  432. {
  433. DATA(self)->stream = fp;
  434. }
  435. else
  436. {
  437. printf("%i:%s\n", errno, strerror(errno));
  438. IoState_error_(IOSTATE, m, "unable to reopen to file '%s' with mode %s.", UTF8CSTRING(DATA(self)->path), CSTRING(DATA(self)->mode));
  439. fclose(fp);
  440. }
  441. }
  442. return self;
  443. }
  444. IO_METHOD(IoFile, popen)
  445. {
  446. /*doc File popen
  447. Open the file as a pipe. Return self.
  448. Closing a popen'ed file sets exitStatus or termSignal
  449. to reflect the status or cause of the child processes' termination.
  450. */
  451. DATA(self)->flags = IOFILE_FLAGS_PIPE;
  452. if (IoMessage_argCount(m) > 0)
  453. {
  454. DATA(self)->path = IOREF(IoMessage_locals_symbolArgAt_(m, locals, 0));
  455. }
  456. if (DATA(self)->stream)
  457. {
  458. IoFile_justClose(self);
  459. }
  460. #if defined(SANE_POPEN)
  461. DATA(self)->mode = IOREF(IOSYMBOL("a+"));
  462. DATA(self)->stream = popen(UTF8CSTRING(DATA(self)->path), "r+");
  463. #elif defined(__SYMBIAN32__)
  464. /* Symbian does not implement popen.
  465. * (There is popen3() but it is "internal and not intended for use.")
  466. */
  467. DATA(self)->mode = IOREF(IOSYMBOL("r"));
  468. DATA(self)->stream = NULL;
  469. #else
  470. DATA(self)->mode = IOREF(IOSYMBOL("r"));
  471. DATA(self)->stream = popen(UTF8CSTRING(DATA(self)->path), "r");
  472. #endif
  473. if (DATA(self)->stream == NULL)
  474. {
  475. IoState_error_(IOSTATE, m, "error executing file path '%s'", UTF8CSTRING(DATA(self)->path));
  476. }
  477. return self;
  478. }
  479. IO_METHOD(IoFile, close)
  480. {
  481. /*doc File close
  482. Closes the receiver if open, otherwise does nothing. Returns self.
  483. When the file was opened via popen, sets either exitStatus or
  484. termSignal to the exit status on normal exit, or the signal causing
  485. abnormal termination.
  486. */
  487. IoFile_justClose(self);
  488. return self;
  489. }
  490. IO_METHOD(IoFile, flush)
  491. {
  492. /*doc File flush
  493. Forces any buffered data to be written to disk. Returns self.
  494. */
  495. fflush(DATA(self)->stream);
  496. return self;
  497. }
  498. IoObject *IoFile_rawAsString(IoFile *self)
  499. {
  500. UArray *ba = UArray_new();
  501. if (UArray_readFromFilePath_(ba, IoSeq_rawUArray(DATA(self)->path)) == 1)
  502. {
  503. return IoState_symbolWithUArray_copy_(IOSTATE, ba, 0);
  504. }
  505. else
  506. {
  507. UArray_free(ba);
  508. IoState_error_(IOSTATE, NULL, "unable to read file '%s'", UTF8CSTRING(DATA(self)->path));
  509. }
  510. return IONIL(self);
  511. }
  512. IO_METHOD(IoFile, contents)
  513. {
  514. /*doc File contents
  515. Returns contents of the file as a mutable Sequence of bytes.
  516. */
  517. UArray *ba = UArray_new();
  518. long result = -1;
  519. if (DATA(self)->stream == stdin)
  520. {
  521. result = UArray_readFromCStream_(ba, DATA(self)->stream);
  522. }
  523. else
  524. {
  525. result = UArray_readFromFilePath_(ba, IoSeq_rawUArray(DATA(self)->path));
  526. }
  527. if (result != -1)
  528. {
  529. return IoSeq_newWithUArray_copy_(IOSTATE, ba, 0);
  530. }
  531. else
  532. {
  533. UArray_free(ba);
  534. IoState_error_(IOSTATE, m, "unable to read file '%s'", UTF8CSTRING(DATA(self)->path));
  535. }
  536. return IONIL(self);
  537. }
  538. IO_METHOD(IoFile, asBuffer)
  539. {
  540. /*doc File asBuffer
  541. Opens the receiver in read only mode, reads the whole
  542. contents of the file into a buffer object, closes the file and returns the buffer.
  543. */
  544. UArray *ba = UArray_new();
  545. long result = UArray_readFromFilePath_(ba, IoSeq_rawUArray(DATA(self)->path));
  546. if (-1 != result)
  547. {
  548. return IoSeq_newWithUArray_copy_(IOSTATE, ba, 0);
  549. }
  550. else
  551. {
  552. UArray_free(ba);
  553. IoState_error_(IOSTATE, m, "unable to read file '%s'", UTF8CSTRING(DATA(self)->path));
  554. }
  555. return IONIL(self);
  556. }
  557. IO_METHOD(IoFile, exists)
  558. {
  559. /*doc File exists(optionalPath)
  560. Returns true if the file path exists, and false otherwise.
  561. If optionalPath string is provided, it tests the existance of that path instead.
  562. */
  563. IoSymbol *path;
  564. if (IoMessage_argCount(m) > 0)
  565. {
  566. path = IoMessage_locals_symbolArgAt_(m, locals, 0);
  567. }
  568. else
  569. {
  570. path = DATA(self)->path;
  571. }
  572. return IOBOOL(self, fileExists(UTF8CSTRING(path)));
  573. }
  574. IO_METHOD(IoFile, remove)
  575. {
  576. /*doc File remove
  577. Removes the file specified by the receiver's path.
  578. Raises an error if the file exists but is not removed. Returns self.
  579. */
  580. int error = 0;
  581. #if defined(__SYMBIAN32__)
  582. error = -1;
  583. #elif defined(_MSC_VER) || defined(__MINGW32__)
  584. if(IoFile_justExists(self))
  585. {
  586. if(ISTRUE(IoFile_isDirectory(self, locals, m)))
  587. {
  588. error = rmdir(UTF8CSTRING(DATA(self)->path));
  589. }
  590. else
  591. {
  592. error = unlink(UTF8CSTRING(DATA(self)->path));
  593. }
  594. }
  595. #else
  596. error = remove(UTF8CSTRING(DATA(self)->path));
  597. #endif
  598. if (error && IoFile_justExists(self))
  599. {
  600. IoState_error_(IOSTATE, m, "error removing file '%s'", UTF8CSTRING(DATA(self)->path));
  601. }
  602. return self;
  603. }
  604. IO_METHOD(IoFile, truncateToSize)
  605. {
  606. /*doc File truncateToSize(numberOfBytes)
  607. Truncates the file's size to the numberOfBytes. Returns self.
  608. */
  609. long newSize = IoMessage_locals_longArgAt_(m, locals, 0);
  610. truncate(UTF8CSTRING(DATA(self)->path), newSize);
  611. return self;
  612. }
  613. IO_METHOD(IoFile, moveTo_)
  614. {
  615. /*doc File moveTo(pathString)
  616. Moves the file specified by the receiver's path to the
  617. new path pathString. Raises a File doesNotExist exception if the
  618. file does not exist or a File nameConflict exception if the file
  619. nameString already exists.
  620. */
  621. IoSymbol *newPath = IoMessage_locals_symbolArgAt_(m, locals, 0);
  622. const char *fromPath = UTF8CSTRING(DATA(self)->path);
  623. const char *toPath = UTF8CSTRING(newPath);
  624. if(strcmp(fromPath, toPath) != 0)
  625. {
  626. int error;
  627. remove(toPath); // to make sure we do not get an error
  628. error = rename(fromPath, toPath);
  629. if (error)
  630. {
  631. IoState_error_(IOSTATE, m, "error moving file '%s' to '%s'", fromPath, toPath);
  632. }
  633. }
  634. return self;
  635. }
  636. IO_METHOD(IoFile, write)
  637. {
  638. /*doc File write(aSequence1, aSequence2, ...)
  639. Writes the arguments to the receiver file. Returns self.
  640. */
  641. int i;
  642. IoFile_assertOpen(self, locals, m);
  643. IoFile_assertWrite(self, locals, m);
  644. for (i = 0; i < IoMessage_argCount(m); i ++)
  645. {
  646. IoSymbol *string = IoMessage_locals_seqArgAt_(m, locals, i);
  647. UArray_writeToCStream_(IoSeq_rawUArray(string), DATA(self)->stream);
  648. if (ferror(DATA(self)->stream) != 0)
  649. {
  650. IoState_error_(IOSTATE, m, "error writing to file '%s'",
  651. UTF8CSTRING(DATA(self)->path));
  652. }
  653. }
  654. return self;
  655. }
  656. IO_METHOD(IoFile, readLines)
  657. {
  658. /*doc File readLines
  659. Returns list containing all lines in the file.
  660. */
  661. IoState *state = IOSTATE;
  662. if (!DATA(self)->stream)
  663. {
  664. IoFile_openForReading(self, locals, m);
  665. }
  666. IoFile_assertOpen(self, locals, m);
  667. {
  668. IoList *lines = IoList_new(state);
  669. IoObject *newLine;
  670. IoState_pushRetainPool(state);
  671. for (;;)
  672. {
  673. IoState_clearTopPool(state);
  674. newLine = IoFile_readLine(self, locals, m);
  675. if (ISNIL(newLine))
  676. {
  677. break;
  678. }
  679. IoList_rawAppend_(lines, newLine);
  680. }
  681. IoState_popRetainPool(state);
  682. return lines;
  683. }
  684. }
  685. IO_METHOD(IoFile, readLine)
  686. {
  687. /*doc File readLine
  688. Reads the next line of the file and returns it as a
  689. string without the return character. Returns Nil if the
  690. end of the file has been reached.
  691. */
  692. //char *path = UTF8CSTRING(DATA(self)->path); // tmp for debugging
  693. IoFile_assertOpen(self, locals, m);
  694. if (feof(DATA(self)->stream) != 0)
  695. {
  696. clearerr(DATA(self)->stream);
  697. return IONIL(self);
  698. }
  699. else
  700. {
  701. UArray *ba = UArray_new();
  702. int error;
  703. unsigned char didRead = UArray_readLineFromCStream_(ba, DATA(self)->stream);
  704. if (!didRead)
  705. {
  706. UArray_free(ba);
  707. return IONIL(self);
  708. }
  709. error = ferror(DATA(self)->stream);
  710. if (error != 0)
  711. {
  712. UArray_free(ba);
  713. clearerr(DATA(self)->stream);
  714. IoState_error_(IOSTATE, m, "error reading from file '%s'", UTF8CSTRING(DATA(self)->path));
  715. return IONIL(self);
  716. }
  717. return IoSeq_newWithUArray_copy_(IOSTATE, ba, 0);
  718. /*return IoState_symbolWithUArray_copy_(IOSTATE, ba, 0);*/
  719. }
  720. }
  721. UArray *IoFile_readUArrayOfLength_(IoFile *self, IoObject *locals, IoMessage *m)
  722. {
  723. size_t length = IoMessage_locals_sizetArgAt_(m, locals, 0);
  724. UArray *ba = UArray_new();
  725. IoFile_assertOpen(self, locals, m);
  726. UArray_readNumberOfItems_fromCStream_(ba, length, DATA(self)->stream);
  727. if (ferror(DATA(self)->stream) != 0)
  728. {
  729. clearerr(DATA(self)->stream);
  730. UArray_free(ba);
  731. IoState_error_(IOSTATE, m, "error reading file '%s'", UTF8CSTRING(DATA(self)->path));
  732. }
  733. if (!UArray_size(ba))
  734. {
  735. UArray_free(ba);
  736. return NULL;
  737. }
  738. return ba;
  739. }
  740. IO_METHOD(IoFile, readToBufferLength)
  741. {
  742. /*doc File readToBufferLength(aBuffer, aNumber)
  743. Reads at most aNumber number of items and appends them to aBuffer.
  744. Returns number of items read.
  745. */
  746. IoSeq *buffer = IoMessage_locals_mutableSeqArgAt_(m, locals, 0);
  747. size_t length = IoMessage_locals_longArgAt_(m, locals, 1);
  748. UArray *ba = IoSeq_rawUArray(buffer);
  749. size_t itemsRead = UArray_readNumberOfItems_fromCStream_(ba, length, DATA(self)->stream);
  750. return IONUMBER(itemsRead);
  751. }
  752. IO_METHOD(IoFile, readBufferOfLength_)
  753. {
  754. /*doc File readBufferOfLength(aNumber)
  755. Reads a Buffer of the specified length and returns it.
  756. Returns Nil if the end of the file has been reached.
  757. */
  758. UArray *ba = IoFile_readUArrayOfLength_(self, locals, m);
  759. if (!ba)
  760. {
  761. return IONIL(self);
  762. }
  763. return IoSeq_newWithUArray_copy_(IOSTATE, ba, 0);
  764. }
  765. IO_METHOD(IoFile, readStringOfLength_)
  766. {
  767. /*doc File readStringOfLength(aNumber)
  768. Reads a String of the specified length and returns it.
  769. Returns Nil if the end of the file has been reached.
  770. */
  771. UArray *ba = IoFile_readUArrayOfLength_(self, locals, m);
  772. if (!ba)
  773. {
  774. return IONIL(self);
  775. }
  776. return IoState_symbolWithUArray_copy_(IOSTATE, ba, 0);
  777. }
  778. IO_METHOD(IoFile, rewind)
  779. {
  780. /*doc File rewind
  781. Sets the file position pointer to the beginning of the file.
  782. */
  783. IoFile_assertOpen(self, locals, m);
  784. if (DATA(self)->stream)
  785. {
  786. rewind(DATA(self)->stream);
  787. }
  788. return self;
  789. }
  790. IO_METHOD(IoFile, position_)
  791. {
  792. /*doc File setPosition(aNumber)
  793. Sets the file position pointer to the byte specified by aNumber. Returns self.
  794. */
  795. long pos = IoMessage_locals_longArgAt_(m, locals, 0);
  796. IoFile_assertOpen(self, locals, m);
  797. if (fseek(DATA(self)->stream, pos, 0) != 0)
  798. {
  799. IoState_error_(IOSTATE, m, "unable to set position %i file path '%s'",
  800. (int)pos, UTF8CSTRING(DATA(self)->path));
  801. }
  802. return self;
  803. }
  804. IO_METHOD(IoFile, position)
  805. {
  806. /*doc File position
  807. Returns the current file pointer byte position as a Number.
  808. */
  809. IoFile_assertOpen(self, locals, m);
  810. return IONUMBER(ftell(DATA(self)->stream));
  811. }
  812. IO_METHOD(IoFile, positionAtEnd)
  813. {
  814. /*doc File positionAtEnd
  815. Sets the file position pointer to the end of the file.
  816. */
  817. IoFile_assertOpen(self, locals, m);
  818. if (DATA(self)->stream)
  819. {
  820. fseek(DATA(self)->stream, 0, SEEK_END);
  821. }
  822. return self;
  823. }
  824. IO_METHOD(IoFile, isAtEnd)
  825. {
  826. /*doc File isAtEnd
  827. Returns true if the file is at its end. Otherwise returns false.
  828. */
  829. IoFile_assertOpen(self, locals, m);
  830. return IOBOOL(self, feof(DATA(self)->stream) != 0);
  831. }
  832. IO_METHOD(IoFile, size)
  833. {
  834. /*doc File size
  835. Returns the file size in bytes.
  836. */
  837. FILE *fp = fopen(UTF8CSTRING(DATA(self)->path), "r");
  838. if (fp)
  839. {
  840. long fileSize;
  841. fseek(fp, 0, SEEK_END);
  842. fileSize = ftell(fp);
  843. fclose(fp);
  844. return IONUMBER(fileSize);
  845. }
  846. else
  847. {
  848. IoState_error_(IOSTATE, m, "unable to open file '%s'", UTF8CSTRING(DATA(self)->path));
  849. }
  850. return IONIL(self);
  851. }
  852. IO_METHOD(IoFile, isOpen)
  853. {
  854. /*doc File isOpen
  855. Returns self if the file is open. Otherwise returns Nil.
  856. */
  857. return IOBOOL(self, DATA(self)->stream != 0);
  858. }
  859. IO_METHOD(IoFile, assertOpen)
  860. {
  861. if (!DATA(self)->stream)
  862. {
  863. IoState_error_(IOSTATE, m, "file '%s' not yet open", UTF8CSTRING(DATA(self)->path));
  864. }
  865. return self;
  866. }
  867. IO_METHOD(IoFile, assertWrite)
  868. {
  869. char *mode = IoSeq_asCString(DATA(self)->mode);
  870. if ((strcmp(mode, "r+")) && (strcmp(mode, "a+")) && (strcmp(mode, "w")))
  871. {
  872. IoState_error_(IOSTATE, m, "file '%s' not open for write", UTF8CSTRING(DATA(self)->path));
  873. }
  874. return self;
  875. }
  876. IO_METHOD(IoFile, at)
  877. {
  878. /*doc File at(aNumber)
  879. Returns a Number containing the byte at the specified
  880. byte index or Nil if the index is out of bounds.
  881. */
  882. int byte;
  883. IoFile_assertOpen(self, locals, m);
  884. IoFile_position_(self, locals, m); /* works since first arg is the same */
  885. byte = fgetc(DATA(self)->stream);
  886. if (byte == EOF)
  887. {
  888. return IONIL(self);
  889. }
  890. return IONUMBER(byte);
  891. }
  892. IO_METHOD(IoFile, atPut)
  893. {
  894. /*doc File atPut(positionNumber, byteNumber)
  895. Writes the byte value of byteNumber to the file position
  896. positionNumber. Returns self.
  897. */
  898. int c = IoMessage_locals_intArgAt_(m, locals, 1);
  899. IoFile_assertOpen(self, locals, m);
  900. IoFile_assertWrite(self, locals, m);
  901. IoFile_position_(self, locals, m); // works since first arg is the same
  902. if (fputc(c, DATA(self)->stream) == EOF)
  903. {
  904. int pos = IoMessage_locals_intArgAt_(m, locals, 0); // BUG - this may not be the same when evaled
  905. IoState_error_(IOSTATE, m, "error writing to position %i in file '%s'", pos, UTF8CSTRING(DATA(self)->path));
  906. }
  907. return self;
  908. }
  909. IO_METHOD(IoFile, foreach)
  910. {
  911. /*doc File foreach(optionalIndex, value, message)
  912. For each byte, set index to the index of the byte
  913. and value to the number containing the byte value and execute aMessage.
  914. Example usage:
  915. <p>
  916. <pre>
  917. aFile foreach(i, v, writeln("byte at ", i, " is ", v))
  918. aFile foreach(v, writeln("byte ", v))
  919. </pre>
  920. */
  921. IoObject *result;
  922. IoSymbol *indexSlotName, *characterSlotName;
  923. IoMessage *doMessage;
  924. int i = 0;
  925. IoFile_assertOpen(self, locals, m);
  926. result = IONIL(self);
  927. IoMessage_foreachArgs(m, self, &indexSlotName, &characterSlotName, &doMessage);
  928. for (;;)
  929. {
  930. int c = getc(DATA(self)->stream);
  931. if (c == EOF)
  932. {
  933. break;
  934. }
  935. if (indexSlotName)
  936. {
  937. IoObject_setSlot_to_(locals, indexSlotName, IONUMBER(i));
  938. }
  939. IoObject_setSlot_to_(locals, characterSlotName, IONUMBER(c));
  940. result = IoMessage_locals_performOn_(doMessage, locals, locals);
  941. if (IoState_handleStatus(IOSTATE))
  942. {
  943. break;
  944. }
  945. i ++;
  946. }
  947. return result;
  948. }
  949. IO_METHOD(IoFile, foreachLine)
  950. {
  951. /*doc File foreachLine(optionalLineNumber, line, message)
  952. For each line, set index to the line number of the line
  953. and line and execute aMessage.
  954. Example usage:
  955. <pre>
  956. aFile foreachLine(i, v, writeln("Line ", i, ": ", v))
  957. aFile foreach(v, writeln("Line: ", v))
  958. </pre>
  959. */
  960. IoObject *result;
  961. IoSymbol *indexSlotName, *lineSlotName;
  962. IoMessage *doMessage;
  963. IoObject *newLine;
  964. int i = 0;
  965. IoState *state;
  966. IoFile_assertOpen(self, locals, m);
  967. IoMessage_foreachArgs(m, self, &indexSlotName, &lineSlotName, &doMessage);
  968. result = IONIL(self);
  969. state = IOSTATE;
  970. IoState_pushRetainPool(state);
  971. for (;;)
  972. {
  973. IoState_clearTopPool(state);
  974. newLine = IoFile_readLine(self, locals, m);
  975. if (ISNIL(newLine))
  976. {
  977. break;
  978. }
  979. if (indexSlotName)
  980. {
  981. IoObject_setSlot_to_(locals, indexSlotName, IONUMBER(i));
  982. }
  983. IoObject_setSlot_to_(locals, lineSlotName, newLine);
  984. result = IoMessage_locals_performOn_(doMessage, locals, locals);
  985. if (IoState_handleStatus(IOSTATE))
  986. {
  987. break;
  988. }
  989. i ++;
  990. }
  991. IoState_popRetainPool(state);
  992. return result;
  993. }
  994. /*
  995. IO_METHOD(IoFile, makeUnbuffered)
  996. {
  997. // doc File makeUnbuffered Sets the file's stream to be unbuffered. Returns self.
  998. setvbuf(DATA(self)->stream, NULL, _IONBF, 0);
  999. // this doesn't work on stdin and there is no OS neutral way to get unbuffered input
  1000. return self;
  1001. }
  1002. IO_METHOD(IoFile, makeLineBuffered)
  1003. {
  1004. // doc File makeLineBuffered Sets the file's stream to be line buffered. Returns self.
  1005. setvbuf(DATA(self)->stream, NULL, _IOLBF, 0);
  1006. return self;
  1007. }
  1008. IO_METHOD(IoFile, makeFullyBuffered)
  1009. {
  1010. // doc File makeFullyBuffered Sets the file's stream to be fully buffered. Returns self.
  1011. setvbuf(DATA(self)->stream, NULL, _IOFBF, 0);
  1012. return self;
  1013. }
  1014. */