1 |
/* |
2 |
* HT Editor |
3 |
* stream.cc |
4 |
* |
5 |
* Copyright (C) 1999-2002 Stefan Weyergraf |
6 |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License version 2 as |
9 |
* published by the Free Software Foundation. |
10 |
* |
11 |
* This program is distributed in the hope that it will be useful, |
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU General Public License |
17 |
* along with this program; if not, write to the Free Software |
18 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 |
*/ |
20 |
|
21 |
#include <cerrno> |
22 |
#include <fcntl.h> |
23 |
#include <limits.h> |
24 |
#include <new> |
25 |
#include <cstring> |
26 |
#include <cstdio> |
27 |
#include <cstdlib> |
28 |
#include <sys/stat.h> /* for mode definitions */ |
29 |
#include <sys/types.h> /* for mode definitions */ |
30 |
#include <unistd.h> |
31 |
|
32 |
#include "debug.h" |
33 |
#include "except.h" |
34 |
#include "system/sys.h" |
35 |
#include "snprintf.h" |
36 |
#include "stream.h" |
37 |
|
38 |
/* |
39 |
* Listener |
40 |
*/ |
41 |
#if 0 |
42 |
class Listener: public Object { |
43 |
public: |
44 |
StreamEventListener *listener; |
45 |
StreamEvent notify_mask; |
46 |
|
47 |
Listener(StreamEventListener *l, StreamEvent nmask) |
48 |
{ |
49 |
listener = l; |
50 |
notify_mask = nmask; |
51 |
} |
52 |
|
53 |
/* extends Object */ |
54 |
virtual int compareTo(const Object *obj) const |
55 |
{ |
56 |
return ((Listener*)obj)->listener == listener ? 0 : 1; |
57 |
} |
58 |
}; |
59 |
#endif |
60 |
/* |
61 |
* Stream |
62 |
*/ |
63 |
#define STREAM_COPYBUF_SIZE (64*1024) |
64 |
|
65 |
Stream::Stream() |
66 |
{ |
67 |
mAccessMode = IOAM_NULL; |
68 |
// mListeners = NULL; |
69 |
} |
70 |
|
71 |
Stream::~Stream() |
72 |
{ |
73 |
// if (mListeners) delete mListeners; |
74 |
} |
75 |
/* |
76 |
void Stream::addEventListener(StreamEventListener *l, StreamEvent mask) |
77 |
{ |
78 |
if (!mListeners) mListeners = new Array(true); |
79 |
*mListeners += new Listener(l, mask); |
80 |
} |
81 |
*/ |
82 |
void Stream::checkAccess(IOAccessMode mask) |
83 |
{ |
84 |
if (mAccessMode & mask != mask) throw IOException(EACCES); |
85 |
} |
86 |
|
87 |
/** |
88 |
* Copy (whole) stream to another (i.e. copy until EOF) |
89 |
* @param stream Stream to copy this Stream to |
90 |
* @returns number of bytes copied |
91 |
*/ |
92 |
uint Stream::copyAllTo(Stream *stream) |
93 |
{ |
94 |
byte *buf = new byte[STREAM_COPYBUF_SIZE]; |
95 |
uint result = 0; |
96 |
uint r, t; |
97 |
do { |
98 |
uint k = STREAM_COPYBUF_SIZE; |
99 |
r = read(buf, k); |
100 |
ASSERT(r <= k); |
101 |
t = stream->write(buf, r); |
102 |
ASSERT(t <= r); |
103 |
result += t; |
104 |
if (t != k) break; |
105 |
} while (t); |
106 |
delete[] buf; |
107 |
return result; |
108 |
} |
109 |
|
110 |
/** |
111 |
* Copy (partial) stream to another |
112 |
* @param stream Stream to copy this Stream to |
113 |
* @param count maximum number of bytes to copy |
114 |
* @returns number of bytes copied |
115 |
*/ |
116 |
uint Stream::copyTo(Stream *stream, uint count) |
117 |
{ |
118 |
byte *buf = new byte[STREAM_COPYBUF_SIZE]; |
119 |
uint result = 0; |
120 |
while (count) { |
121 |
uint k = STREAM_COPYBUF_SIZE; |
122 |
if (k > count) k = count; |
123 |
uint r = read(buf, k); |
124 |
ASSERT(r <= k); |
125 |
uint t = stream->write(buf, r); |
126 |
ASSERT(t <= r); |
127 |
count -= t; |
128 |
result += t; |
129 |
if (t != k) break; |
130 |
} |
131 |
delete[] buf; |
132 |
return result; |
133 |
} |
134 |
|
135 |
/** |
136 |
* Get access-mode |
137 |
*/ |
138 |
IOAccessMode Stream::getAccessMode() const |
139 |
{ |
140 |
return mAccessMode; |
141 |
} |
142 |
|
143 |
/** |
144 |
* Get descriptive name |
145 |
*/ |
146 |
String &Stream::getDesc(String &result) const |
147 |
{ |
148 |
result = ""; |
149 |
return result; |
150 |
} |
151 |
/* |
152 |
void Stream::notifyListeners(StreamEvent event,...) |
153 |
{ |
154 |
if (!mListeners) return; |
155 |
foreach(Listener, l, *mListeners, |
156 |
if (l->notify_mask & event) { |
157 |
va_list ap; |
158 |
va_start(ap, event); |
159 |
l->listener->handleEvent(event, ap); |
160 |
va_end(ap); |
161 |
} |
162 |
); |
163 |
} |
164 |
*/ |
165 |
/** |
166 |
* Set access-mode |
167 |
*/ |
168 |
int Stream::setAccessMode(IOAccessMode mode) |
169 |
{ |
170 |
mAccessMode = mode; |
171 |
return 0; |
172 |
} |
173 |
|
174 |
/** |
175 |
* Read from stream. |
176 |
* Read up to <i>size</i> bytes from stream into <i>buf</i>. |
177 |
* If less than <i>size</i> bytes are read, the exact number is |
178 |
* returned and a (temporary) end-of-file (EOF) is encountered. |
179 |
* |
180 |
* @param buf pointer to bytes to read |
181 |
* @param size number of bytes to read |
182 |
* @returns number of bytes read |
183 |
* @throws IOException |
184 |
*/ |
185 |
uint Stream::read(void *buf, uint size) |
186 |
{ |
187 |
return 0; |
188 |
} |
189 |
|
190 |
/** |
191 |
* Exact read from stream. |
192 |
* Read exactly <i>size</i> bytes from stream into <i>buf</i>. |
193 |
* If less than <i>size</i> bytes are read, IOException is thrown. |
194 |
* |
195 |
* @param buf pointer to bytes to read |
196 |
* @param size number of bytes to read |
197 |
* @throws IOException |
198 |
*/ |
199 |
//#include "snprintf.h" |
200 |
void Stream::readx(void *buf, uint size) |
201 |
{ |
202 |
// File *f = dynamic_cast<File*>(this); |
203 |
// FileOfs t = f ? f->tell() : mkfofs(0); |
204 |
if (read(buf, size) != size) { |
205 |
// FileOfs sz = f ? f->getSize() : mkfofs(0); |
206 |
// ht_printf("readx failed, ofs = 0x%qx, size = %d (file size 0x%qx)\n", t, size, sz); |
207 |
throw IOException(EIO); |
208 |
} |
209 |
} |
210 |
/* |
211 |
void Stream::removeEventListener(StreamEventListener *l) |
212 |
{ |
213 |
Listener t(l, SEV_NULL); |
214 |
bool b = (*mListeners -= &t); |
215 |
ASSERT(b); |
216 |
} |
217 |
*/ |
218 |
/** |
219 |
* Write to stream. |
220 |
* Write up to <i>size</i> bytes from <i>buf</i> into stream. |
221 |
* If less than <i>size</i> bytes are written, the exact number is |
222 |
* returned and a (temporary) end-of-file (EOF) is encountered. |
223 |
* |
224 |
* @param buf pointer to bytes to write |
225 |
* @param size number of bytes to write |
226 |
* @returns number of bytes written |
227 |
* @throws IOException |
228 |
*/ |
229 |
uint Stream::write(const void *buf, uint size) |
230 |
{ |
231 |
return 0; |
232 |
} |
233 |
|
234 |
/** |
235 |
* Exact write to stream. |
236 |
* Write exactly <i>size</i> bytes from <i>buf</i> into stream. |
237 |
* If less than <i>size</i> bytes are written, IOException is thrown. |
238 |
* |
239 |
* @param buf pointer to bytes to write |
240 |
* @param size number of bytes to write |
241 |
* @throws IOException |
242 |
*/ |
243 |
void Stream::writex(const void *buf, uint size) |
244 |
{ |
245 |
if (write(buf, size) != size) throw IOException(EIO); |
246 |
} |
247 |
|
248 |
/* |
249 |
* StreamLayer |
250 |
*/ |
251 |
|
252 |
StreamLayer::StreamLayer(Stream *s, bool own) : Stream() |
253 |
{ |
254 |
mStream = s; |
255 |
mOwnStream = own; |
256 |
} |
257 |
|
258 |
StreamLayer::~StreamLayer() |
259 |
{ |
260 |
if (mOwnStream) delete mStream; |
261 |
} |
262 |
|
263 |
IOAccessMode StreamLayer::getAccessMode() const |
264 |
{ |
265 |
return mStream->getAccessMode(); |
266 |
} |
267 |
|
268 |
String &StreamLayer::getDesc(String &result) const |
269 |
{ |
270 |
return mStream->getDesc(result); |
271 |
} |
272 |
|
273 |
int StreamLayer::setAccessMode(IOAccessMode mode) |
274 |
{ |
275 |
return mStream->setAccessMode(mode); |
276 |
} |
277 |
|
278 |
uint StreamLayer::read(void *buf, uint size) |
279 |
{ |
280 |
return mStream->read(buf, size); |
281 |
} |
282 |
|
283 |
uint StreamLayer::write(const void *buf, uint size) |
284 |
{ |
285 |
return mStream->write(buf, size); |
286 |
} |
287 |
|
288 |
Stream *StreamLayer::getLayered() const |
289 |
{ |
290 |
return mStream; |
291 |
} |
292 |
|
293 |
void StreamLayer::setLayered(Stream *newLayered, bool ownNewLayered) |
294 |
{ |
295 |
mStream = newLayered; |
296 |
mOwnStream = ownNewLayered; |
297 |
} |
298 |
|
299 |
/* |
300 |
* A File |
301 |
*/ |
302 |
|
303 |
File::File() |
304 |
{ |
305 |
mcount = 0; |
306 |
} |
307 |
|
308 |
#define FILE_TRANSFER_BUFSIZE 4*1024 |
309 |
/** |
310 |
* Low-level control function. |
311 |
* @param cmd file control command number |
312 |
* @returns 0 on success, POSIX.1 I/O error code on error |
313 |
*/ |
314 |
int File::cntl(uint cmd, ...) |
315 |
{ |
316 |
va_list vargs; |
317 |
va_start(vargs, cmd); |
318 |
int ret = vcntl(cmd, vargs); |
319 |
va_end(vargs); |
320 |
return ret; |
321 |
} |
322 |
|
323 |
void fileMove(File *file, FileOfs src, FileOfs dest, FileOfs size) |
324 |
{ |
325 |
if (dest < src) { |
326 |
char tbuf[FILE_TRANSFER_BUFSIZE]; |
327 |
while (size != 0) { |
328 |
FileOfs k = size; |
329 |
if (k > sizeof tbuf) k = sizeof tbuf; |
330 |
file->seek(src); |
331 |
file->readx(tbuf, k); |
332 |
file->seek(dest); |
333 |
file->writex(tbuf, k); |
334 |
src += k; |
335 |
dest += k; |
336 |
size -= k; |
337 |
} |
338 |
} else if (dest > src) { |
339 |
src += size; |
340 |
dest += size; |
341 |
char tbuf[FILE_TRANSFER_BUFSIZE]; |
342 |
while (size != 0) { |
343 |
FileOfs k = size; |
344 |
if (k > sizeof tbuf) k = sizeof tbuf; |
345 |
src -= k; |
346 |
dest -= k; |
347 |
file->seek(src); |
348 |
file->readx(tbuf, k); |
349 |
file->seek(dest); |
350 |
file->writex(tbuf, k); |
351 |
size -= k; |
352 |
} |
353 |
} |
354 |
} |
355 |
|
356 |
/** |
357 |
* Delete bytes from file. |
358 |
* Delete <i>size</i> bytes at current file pointer and shorten file |
359 |
* accordingly. |
360 |
Does not modify the current file pointer. |
361 |
* |
362 |
* @param size number of bytes to delete |
363 |
* @throws IOException |
364 |
*/ |
365 |
void File::del(uint size) |
366 |
{ |
367 |
FileOfs t = tell(); |
368 |
FileOfs o = t+size; |
369 |
if (o > getSize()) throw IOException(EINVAL); |
370 |
FileOfs s = getSize()-o; |
371 |
fileMove(this, o, t, s); |
372 |
truncate(getSize()-size); |
373 |
seek(t); |
374 |
} |
375 |
|
376 |
/** |
377 |
* Extend file. |
378 |
* Extend file to new size <i>newsize</i>. |
379 |
* The current file pointer is undefined (but valid) after this operation. |
380 |
* |
381 |
* @param newsize new extended file size |
382 |
* @throws IOException |
383 |
*/ |
384 |
void File::extend(FileOfs newsize) |
385 |
{ |
386 |
if (getSize() > newsize) throw IOException(EINVAL); |
387 |
if (getSize() == newsize) return; |
388 |
|
389 |
FileOfs save_ofs = tell(); |
390 |
int e = 0; |
391 |
|
392 |
IOAccessMode oldmode = getAccessMode(); |
393 |
if (!(oldmode & IOAM_WRITE)) { |
394 |
int f = setAccessMode(oldmode | IOAM_WRITE); |
395 |
if (f) throw IOException(f); |
396 |
} |
397 |
|
398 |
FileOfs s = getSize(); |
399 |
char buf[FILE_TRANSFER_BUFSIZE]; |
400 |
memset(buf, 0, sizeof buf); |
401 |
newsize -= s; |
402 |
seek(s); |
403 |
while (newsize != 0) { |
404 |
uint k = MIN(sizeof buf, newsize); |
405 |
uint l = write(buf, k); |
406 |
if (l != k) { |
407 |
e = EIO; |
408 |
break; |
409 |
} |
410 |
newsize -= l; |
411 |
} |
412 |
|
413 |
if (!(oldmode & IOAM_WRITE)) { |
414 |
int f = setAccessMode(oldmode); |
415 |
if (f) e = f; |
416 |
} |
417 |
if (e) throw IOException(e); |
418 |
seek(save_ofs); |
419 |
} |
420 |
|
421 |
/** |
422 |
* Get filename. |
423 |
* |
424 |
* @param result String that receives the filename |
425 |
* @returns its argument |
426 |
*/ |
427 |
String &File::getFilename(String &result) const |
428 |
{ |
429 |
result = ""; |
430 |
return result; |
431 |
} |
432 |
|
433 |
/** |
434 |
* Get file size. |
435 |
* |
436 |
* @returns file size |
437 |
*/ |
438 |
FileOfs File::getSize() const |
439 |
{ |
440 |
return 0; |
441 |
} |
442 |
|
443 |
#define FILE_INSERT_BUFSIZE 4*1024 |
444 |
/** |
445 |
* Insert bytes into file. |
446 |
* Insert <i>size</i> bytes from <i>buf</i> at the current file pointer |
447 |
* into the file and extend file accordingly. |
448 |
* |
449 |
* @param buf pointer to buffer that holds at least <i>size</i> bytes |
450 |
* @param size number of bytes to insert |
451 |
* @throws IOException |
452 |
*/ |
453 |
void File::insert(const void *buf, uint size) |
454 |
{ |
455 |
FileOfs t = tell(); |
456 |
FileOfs s = getSize()-t; |
457 |
extend(getSize()+size); |
458 |
fileMove(this, t, t+size, s); |
459 |
seek(t); |
460 |
writex(buf, size); |
461 |
} |
462 |
|
463 |
/** |
464 |
* Get file status in a portable way. |
465 |
* @param s structure that receives the file status |
466 |
*/ |
467 |
void File::pstat(pstat_t &s) const |
468 |
{ |
469 |
s.caps = 0; |
470 |
} |
471 |
|
472 |
/** |
473 |
* Set current file pointer. |
474 |
* @param offset new value for current file pointer |
475 |
*/ |
476 |
void File::seek(FileOfs offset) |
477 |
{ |
478 |
throw NotImplementedException(HERE); |
479 |
} |
480 |
|
481 |
/** |
482 |
* Get current file pointer. |
483 |
* @returns current file pointer |
484 |
*/ |
485 |
FileOfs File::tell() const |
486 |
{ |
487 |
return 0; |
488 |
} |
489 |
|
490 |
/** |
491 |
* Truncate file. |
492 |
* Truncate file to new size <i>newsize</i>. |
493 |
* The current file pointer is undefined (but valid) after this operation. |
494 |
* |
495 |
* @param newsize new truncated file size |
496 |
* @throws IOException |
497 |
*/ |
498 |
void File::truncate(FileOfs newsize) |
499 |
{ |
500 |
if (getSize() < newsize) throw IOException(EINVAL); |
501 |
if (getSize() == newsize) return; |
502 |
|
503 |
throw NotImplementedException(HERE); |
504 |
} |
505 |
|
506 |
/** |
507 |
* Vararg wrapper for cntl() |
508 |
*/ |
509 |
int File::vcntl(uint cmd, va_list vargs) |
510 |
{ |
511 |
switch (cmd) { |
512 |
case FCNTL_GET_MOD_COUNT: { // int &mcount |
513 |
int *mc = va_arg(vargs, int *); |
514 |
*mc = mcount; |
515 |
return 0; |
516 |
} |
517 |
} |
518 |
return ENOSYS; |
519 |
} |
520 |
|
521 |
/* |
522 |
* FileLayer |
523 |
*/ |
524 |
FileLayer::FileLayer(File *f, bool own_f) : File() |
525 |
{ |
526 |
mFile = f; |
527 |
mOwnFile = own_f; |
528 |
} |
529 |
|
530 |
FileLayer::~FileLayer() |
531 |
{ |
532 |
if (mOwnFile) delete mFile; |
533 |
} |
534 |
|
535 |
void FileLayer::del(uint size) |
536 |
{ |
537 |
return mFile->del(size); |
538 |
} |
539 |
|
540 |
void FileLayer::extend(FileOfs newsize) |
541 |
{ |
542 |
return mFile->extend(newsize); |
543 |
} |
544 |
|
545 |
IOAccessMode FileLayer::getAccessMode() const |
546 |
{ |
547 |
return mFile->getAccessMode(); |
548 |
} |
549 |
|
550 |
String &FileLayer::getDesc(String &result) const |
551 |
{ |
552 |
return mFile->getDesc(result); |
553 |
} |
554 |
|
555 |
String &FileLayer::getFilename(String &result) const |
556 |
{ |
557 |
return mFile->getFilename(result); |
558 |
} |
559 |
|
560 |
FileOfs FileLayer::getSize() const |
561 |
{ |
562 |
return mFile->getSize(); |
563 |
} |
564 |
|
565 |
void FileLayer::insert(const void *buf, uint size) |
566 |
{ |
567 |
return mFile->insert(buf, size); |
568 |
} |
569 |
|
570 |
void FileLayer::pstat(pstat_t &s) const |
571 |
{ |
572 |
return mFile->pstat(s); |
573 |
} |
574 |
|
575 |
uint FileLayer::read(void *buf, uint size) |
576 |
{ |
577 |
return mFile->read(buf, size); |
578 |
} |
579 |
|
580 |
void FileLayer::seek(FileOfs offset) |
581 |
{ |
582 |
return mFile->seek(offset); |
583 |
} |
584 |
|
585 |
int FileLayer::setAccessMode(IOAccessMode mode) |
586 |
{ |
587 |
return mFile->setAccessMode(mode); |
588 |
} |
589 |
|
590 |
File *FileLayer::getLayered() const |
591 |
{ |
592 |
return mFile; |
593 |
} |
594 |
|
595 |
void FileLayer::setLayered(File *newLayered, bool ownNewLayered) |
596 |
{ |
597 |
mFile = newLayered; |
598 |
mOwnFile = ownNewLayered; |
599 |
} |
600 |
|
601 |
FileOfs FileLayer::tell() const |
602 |
{ |
603 |
return mFile->tell(); |
604 |
} |
605 |
|
606 |
void FileLayer::truncate(FileOfs newsize) |
607 |
{ |
608 |
return mFile->truncate(newsize); |
609 |
} |
610 |
|
611 |
int FileLayer::vcntl(uint cmd, va_list vargs) |
612 |
{ |
613 |
return mFile->vcntl(cmd, vargs); |
614 |
} |
615 |
|
616 |
uint FileLayer::write(const void *buf, uint size) |
617 |
{ |
618 |
return mFile->write(buf, size); |
619 |
} |
620 |
|
621 |
#if 0 |
622 |
/* |
623 |
* LocalFileFD |
624 |
*/ |
625 |
|
626 |
/** |
627 |
* create open file |
628 |
*/ |
629 |
LocalFileFD::LocalFileFD(const String &aFilename, IOAccessMode am, FileOpenMode om) |
630 |
: File(), mFilename(aFilename) |
631 |
{ |
632 |
mOpenMode = om; |
633 |
fd = -1; |
634 |
own_fd = false; |
635 |
int e = setAccessMode(am); |
636 |
if (e) throw IOException(e); |
637 |
mOpenMode = FOM_EXISTS; |
638 |
} |
639 |
|
640 |
/** |
641 |
* map a file descriptor [fd] |
642 |
*/ |
643 |
LocalFileFD::LocalFileFD(int f, bool own_f, IOAccessMode am) |
644 |
: File() |
645 |
{ |
646 |
mFilename = NULL; |
647 |
fd = f; |
648 |
own_fd = own_f; |
649 |
offset = 0; |
650 |
int e = File::setAccessMode(am); |
651 |
if (e) throw IOException(e); |
652 |
} |
653 |
|
654 |
LocalFileFD::~LocalFileFD() |
655 |
{ |
656 |
if (own_fd && (fd>=0)) ::close(fd); |
657 |
} |
658 |
|
659 |
String &LocalFileFD::getDesc(String &result) const |
660 |
{ |
661 |
result = mFilename; |
662 |
return result; |
663 |
} |
664 |
|
665 |
String &LocalFileFD::getFilename(String &result) const |
666 |
{ |
667 |
result = mFilename; |
668 |
return result; |
669 |
} |
670 |
|
671 |
FileOfs LocalFileFD::getSize() const |
672 |
{ |
673 |
uint t = tell(); |
674 |
uint r = ::lseek(fd, 0, SEEK_END); |
675 |
lseek(fd, t, SEEK_SET); |
676 |
return r; |
677 |
} |
678 |
|
679 |
uint LocalFileFD::read(void *buf, uint size) |
680 |
{ |
681 |
if (!(getAccessMode() & IOAM_READ)) throw IOException(EACCES); |
682 |
errno = 0; |
683 |
uint r = ::read(fd, buf, size); |
684 |
int e = errno; |
685 |
if (e) { |
686 |
off_t r = ::lseek(fd, 0, SEEK_SET); |
687 |
offset = r; |
688 |
if (e != EAGAIN) throw IOException(e); |
689 |
return 0; |
690 |
} else { |
691 |
offset += r; |
692 |
return r; |
693 |
} |
694 |
} |
695 |
|
696 |
void LocalFileFD::seek(FileOfs o) |
697 |
{ |
698 |
if (o == offset) return; |
699 |
off_t r = ::lseek(fd, o, SEEK_SET); |
700 |
offset = r; |
701 |
if (offset != o) throw IOException(EIO); |
702 |
} |
703 |
|
704 |
int LocalFileFD::setAccessMode(IOAccessMode am) |
705 |
{ |
706 |
IOAccessMode orig_access_mode = getAccessMode(); |
707 |
int e = setAccessModeInternal(am); |
708 |
if (e && setAccessModeInternal(orig_access_mode)) |
709 |
throw IOException(e); |
710 |
return e; |
711 |
} |
712 |
|
713 |
int LocalFileFD::setAccessModeInternal(IOAccessMode am) |
714 |
{ |
715 |
//RETRY: |
716 |
if (getAccessMode() == am) return 0; |
717 |
if (fd >= 0) { |
718 |
// must own fd to change its access mode cause we can't |
719 |
// reopen a fd. right ? |
720 |
if (!own_fd) throw NotImplementedException(HERE); |
721 |
// FIXME: race condition here, how to reopen a fd atomically ? |
722 |
close(fd); |
723 |
fd = -1; |
724 |
} |
725 |
File::setAccessMode(IOAM_NULL); |
726 |
|
727 |
int mode = 0; |
728 |
|
729 |
if ((am & IOAM_READ) && (am & IOAM_WRITE)) mode = O_RDWR; |
730 |
else if (am & IOAM_READ) mode = O_RDONLY; |
731 |
else if (am & IOAM_WRITE) mode = O_WRONLY; |
732 |
|
733 |
// mode |= O_BINARY; |
734 |
|
735 |
switch (mOpenMode) { |
736 |
case FOM_APPEND: |
737 |
mode |= O_APPEND; |
738 |
break; |
739 |
case FOM_CREATE: |
740 |
mode |= O_CREAT | O_TRUNC; |
741 |
break; |
742 |
case FOM_EXISTS: |
743 |
; |
744 |
} |
745 |
|
746 |
int e = 0; |
747 |
if (am != IOAM_NULL) { |
748 |
pstat_t s; |
749 |
fd = ::open(mFilename, mode); |
750 |
if (fd < 0) e = errno; |
751 |
if (!e) { |
752 |
own_fd = true; |
753 |
e = sys_pstat_fd(s, fd); |
754 |
if (!e) { |
755 |
if (HT_S_ISDIR(s.mode)) { |
756 |
e = EISDIR; |
757 |
} else if (!HT_S_ISREG(s.mode) && !HT_S_ISBLK(s.mode)) { |
758 |
e = EINVAL; |
759 |
} |
760 |
} |
761 |
} |
762 |
} |
763 |
return e ? e : File::setAccessMode(am); |
764 |
} |
765 |
|
766 |
FileOfs LocalFileFD::tell() const |
767 |
{ |
768 |
return offset; |
769 |
} |
770 |
|
771 |
void LocalFileFD::truncate(FileOfs newsize) |
772 |
{ |
773 |
errno = 0; |
774 |
int e = sys_truncate_fd(fd, newsize); |
775 |
if (errno) e = errno; |
776 |
if (e) throw IOException(e); |
777 |
} |
778 |
|
779 |
int LocalFileFD::vcntl(uint cmd, va_list vargs) |
780 |
{ |
781 |
switch (cmd) { |
782 |
case FCNTL_FLUSH_STAT: { |
783 |
IOAccessMode m = getAccessMode(); |
784 |
int e, f; |
785 |
e = setAccessMode(IOAM_NULL); |
786 |
f = setAccessMode(m); |
787 |
return e ? e : f; |
788 |
} |
789 |
case FCNTL_GET_FD: { // (int &fd) |
790 |
int *pfd = va_arg(vargs, int*); |
791 |
*pfd = fd; |
792 |
return 0; |
793 |
} |
794 |
} |
795 |
return File::vcntl(cmd, vargs); |
796 |
} |
797 |
|
798 |
uint LocalFileFD::write(const void *buf, uint size) |
799 |
{ |
800 |
if (!(getAccessMode() & IOAM_WRITE)) throw IOException(EACCES); |
801 |
errno = 0; |
802 |
uint r = ::write(fd, buf, size); |
803 |
int e = errno; |
804 |
if (e) { |
805 |
off_t r = ::lseek(fd, 0, SEEK_SET); |
806 |
offset = r; |
807 |
if (e != EAGAIN) throw IOException(e); |
808 |
return 0; |
809 |
} else { |
810 |
offset += r; |
811 |
return r; |
812 |
} |
813 |
} |
814 |
#endif |
815 |
|
816 |
/* |
817 |
* StdIoFile |
818 |
*/ |
819 |
|
820 |
/** |
821 |
* create open file |
822 |
*/ |
823 |
LocalFile::LocalFile(const String &aFilename, IOAccessMode am, FileOpenMode om) |
824 |
: File(), mFilename(aFilename) |
825 |
{ |
826 |
mOpenMode = om; |
827 |
file = NULL; |
828 |
own_file = false; |
829 |
offset = 0; |
830 |
int e = setAccessMode(am); |
831 |
if (e) throw IOException(e); |
832 |
mOpenMode = FOM_EXISTS; |
833 |
} |
834 |
|
835 |
/** |
836 |
* map a file stream [FILE*] |
837 |
*/ |
838 |
LocalFile::LocalFile(FILE *f, bool own_f, IOAccessMode am) |
839 |
: File() |
840 |
{ |
841 |
mFilename = NULL; |
842 |
file = f; |
843 |
own_file = own_f; |
844 |
int e = LocalFile::setAccessMode(am); |
845 |
if (e) throw IOException(e); |
846 |
} |
847 |
|
848 |
LocalFile::~LocalFile() |
849 |
{ |
850 |
if (own_file && file) fclose(file); |
851 |
} |
852 |
|
853 |
String &LocalFile::getDesc(String &result) const |
854 |
{ |
855 |
result = mFilename; |
856 |
return result; |
857 |
} |
858 |
|
859 |
String &LocalFile::getFilename(String &result) const |
860 |
{ |
861 |
result = mFilename; |
862 |
return result; |
863 |
} |
864 |
|
865 |
FileOfs LocalFile::getSize() const |
866 |
{ |
867 |
int t = tell(); |
868 |
fseek(file, 0, SEEK_END); |
869 |
int r = ftell(file); |
870 |
fseek(file, t, SEEK_SET); |
871 |
return r; |
872 |
} |
873 |
|
874 |
void LocalFile::pstat(pstat_t &s) const |
875 |
{ |
876 |
sys_pstat(s, mFilename.contentChar()); |
877 |
} |
878 |
|
879 |
uint LocalFile::read(void *buf, uint size) |
880 |
{ |
881 |
if (!(getAccessMode() & IOAM_READ)) throw IOException(EACCES); |
882 |
errno = 0; |
883 |
uint r = fread(buf, 1, size, file); |
884 |
if (errno) throw IOException(errno); |
885 |
offset += r; |
886 |
return r; |
887 |
} |
888 |
|
889 |
void LocalFile::seek(FileOfs o) |
890 |
{ |
891 |
if (o == offset) return; |
892 |
int e = fseek(file, o, SEEK_SET); |
893 |
if (e) throw IOException(e); |
894 |
offset = o; // unreliable for DJGPP |
895 |
} |
896 |
|
897 |
int LocalFile::setAccessMode(IOAccessMode am) |
898 |
{ |
899 |
IOAccessMode orig_access_mode = getAccessMode(); |
900 |
int e = setAccessModeInternal(am); |
901 |
if (e && setAccessModeInternal(orig_access_mode)) |
902 |
throw IOException(e); |
903 |
return e; |
904 |
} |
905 |
|
906 |
int LocalFile::setAccessModeInternal(IOAccessMode am) |
907 |
{ |
908 |
//RETRY: |
909 |
if (getAccessMode() == am) return 0; |
910 |
char *mode = NULL; |
911 |
|
912 |
switch (mOpenMode) { |
913 |
case FOM_APPEND: |
914 |
mode = "ab+"; |
915 |
break; |
916 |
case FOM_CREATE: |
917 |
if (am & IOAM_WRITE) mode = "wb"; |
918 |
if (am & IOAM_READ) mode = "wb+"; |
919 |
break; |
920 |
case FOM_EXISTS: |
921 |
if (am & IOAM_READ) mode = "rb"; |
922 |
if (am & IOAM_WRITE) mode = "rb+"; |
923 |
break; |
924 |
} |
925 |
|
926 |
int e = 0; |
927 |
if (am != IOAM_NULL) { |
928 |
pstat_t s; |
929 |
if (file) { |
930 |
file = freopen(mFilename.contentChar(), mode, file); |
931 |
if (!file) setAccessMode(IOAM_NULL); |
932 |
} else { |
933 |
file = fopen(mFilename.contentChar(), mode); |
934 |
} |
935 |
if (!file) e = errno; |
936 |
if (!e) { |
937 |
own_file = true; |
938 |
e = sys_pstat_fd(s, fileno(file)); |
939 |
if (!e) { |
940 |
if (HT_S_ISDIR(s.mode)) { |
941 |
e = EISDIR; |
942 |
} else if (!HT_S_ISREG(s.mode) && !HT_S_ISBLK(s.mode)) { |
943 |
e = EINVAL; |
944 |
} |
945 |
} |
946 |
} |
947 |
} |
948 |
return e ? e : File::setAccessMode(am); |
949 |
} |
950 |
|
951 |
FileOfs LocalFile::tell() const |
952 |
{ |
953 |
return offset; |
954 |
} |
955 |
|
956 |
void LocalFile::truncate(FileOfs newsize) |
957 |
{ |
958 |
errno = 0; |
959 |
|
960 |
IOAccessMode old_am = getAccessMode(); |
961 |
int e; |
962 |
e = setAccessMode(IOAM_NULL); |
963 |
if (!e) { |
964 |
e = sys_truncate(mFilename.contentChar(), newsize); |
965 |
if (errno) e = errno; |
966 |
} |
967 |
if (!e) e = setAccessMode(old_am); |
968 |
if (e) throw IOException(e); |
969 |
} |
970 |
|
971 |
int LocalFile::vcntl(uint cmd, va_list vargs) |
972 |
{ |
973 |
switch (cmd) { |
974 |
case FCNTL_FLUSH_STAT: { |
975 |
IOAccessMode m = getAccessMode(); |
976 |
int e, f; |
977 |
e = setAccessMode(IOAM_NULL); |
978 |
f = setAccessMode(m); |
979 |
return e ? e : f; |
980 |
} |
981 |
case FCNTL_GET_FD: { // (int &fd) |
982 |
if (file) { |
983 |
int *pfd = va_arg(vargs, int*); |
984 |
*pfd = fileno(file); |
985 |
return 0; |
986 |
} |
987 |
break; |
988 |
} |
989 |
} |
990 |
return File::vcntl(cmd, vargs); |
991 |
} |
992 |
|
993 |
uint LocalFile::write(const void *buf, uint size) |
994 |
{ |
995 |
if (!(getAccessMode() & IOAM_WRITE)) throw IOException(EACCES); |
996 |
errno = 0; |
997 |
uint r = fwrite(buf, 1, size, file); |
998 |
if (errno) throw IOException(errno); |
999 |
offset += r; |
1000 |
return r; |
1001 |
} |
1002 |
|
1003 |
/* |
1004 |
* TempFile |
1005 |
*/ |
1006 |
TempFile::TempFile(uint am) : LocalFile(tmpfile(), true, am) |
1007 |
{ |
1008 |
} |
1009 |
|
1010 |
String &TempFile::getDesc(String &result) const |
1011 |
{ |
1012 |
result = "temporary file"; |
1013 |
return result; |
1014 |
} |
1015 |
|
1016 |
void TempFile::pstat(pstat_t &s) const |
1017 |
{ |
1018 |
s.caps = pstat_size; |
1019 |
s.size = getSize(); |
1020 |
} |
1021 |
|
1022 |
/* |
1023 |
* MemMapFile |
1024 |
*/ |
1025 |
MemMapFile::MemMapFile(void *b, uint s) : ConstMemMapFile(b, s) |
1026 |
{ |
1027 |
} |
1028 |
|
1029 |
uint MemMapFile::write(const void *b, uint size) |
1030 |
{ |
1031 |
if (pos > this->size) return 0; // or throw exception? |
1032 |
if (pos+size > this->size) size = this->size - pos; |
1033 |
memmove(((byte*)buf)+pos, b, size); |
1034 |
pos += size; |
1035 |
return size; |
1036 |
} |
1037 |
|
1038 |
/* |
1039 |
* ConstMemMapFile |
1040 |
*/ |
1041 |
ConstMemMapFile::ConstMemMapFile(const void *b, uint s) |
1042 |
: File() |
1043 |
{ |
1044 |
buf = b; |
1045 |
pos = 0; |
1046 |
size = s; |
1047 |
} |
1048 |
|
1049 |
String &ConstMemMapFile::getDesc(String &result) const |
1050 |
{ |
1051 |
result = "MemMapFile"; |
1052 |
return result; |
1053 |
} |
1054 |
|
1055 |
FileOfs ConstMemMapFile::getSize() const |
1056 |
{ |
1057 |
return size; |
1058 |
} |
1059 |
|
1060 |
uint ConstMemMapFile::read(void *b, uint size) |
1061 |
{ |
1062 |
if (pos > this->size) return 0; |
1063 |
if (pos+size > this->size) size = this->size - pos; |
1064 |
memmove(b, (const byte*)buf+pos, size); |
1065 |
pos += size; |
1066 |
return size; |
1067 |
} |
1068 |
|
1069 |
void ConstMemMapFile::seek(FileOfs offset) |
1070 |
{ |
1071 |
pos = offset; |
1072 |
} |
1073 |
|
1074 |
FileOfs ConstMemMapFile::tell() const |
1075 |
{ |
1076 |
return pos; |
1077 |
} |
1078 |
|
1079 |
/* |
1080 |
* A file layer, representing a cropped version of a file |
1081 |
*/ |
1082 |
CroppedFile::CroppedFile(File *file, bool own_file, FileOfs aCropStart, FileOfs aCropSize) |
1083 |
: FileLayer(file, own_file) |
1084 |
{ |
1085 |
mCropStart = aCropStart; |
1086 |
mHasCropSize = true; |
1087 |
mCropSize = aCropSize; |
1088 |
seek(0); |
1089 |
} |
1090 |
|
1091 |
CroppedFile::CroppedFile(File *file, bool own_file, FileOfs aCropStart) |
1092 |
: FileLayer(file, own_file) |
1093 |
{ |
1094 |
mCropStart = aCropStart; |
1095 |
mHasCropSize = false; |
1096 |
seek(0); |
1097 |
} |
1098 |
|
1099 |
void CroppedFile::extend(FileOfs newsize) |
1100 |
{ |
1101 |
throw IOException(ENOSYS); |
1102 |
} |
1103 |
|
1104 |
String &CroppedFile::getDesc(String &result) const |
1105 |
{ |
1106 |
String s; |
1107 |
if (mHasCropSize) { |
1108 |
result.assignFormat("[->0x%qx,0x%qx] of %y", mCropStart, mCropSize, &FileLayer::getDesc(s)); |
1109 |
} else { |
1110 |
result.assignFormat("[->0x%qx] of %y", mCropStart, &FileLayer::getDesc(s)); |
1111 |
} |
1112 |
return result; |
1113 |
} |
1114 |
|
1115 |
FileOfs CroppedFile::getSize() const |
1116 |
{ |
1117 |
FileOfs lsize = FileLayer::getSize(); |
1118 |
if (lsize < mCropStart) return 0; |
1119 |
lsize -= mCropStart; |
1120 |
if (mHasCropSize) { |
1121 |
if (lsize > mCropSize) lsize = mCropSize; |
1122 |
} |
1123 |
return lsize; |
1124 |
} |
1125 |
|
1126 |
void CroppedFile::pstat(pstat_t &s) const |
1127 |
{ |
1128 |
FileLayer::pstat(s); |
1129 |
if (s.caps & pstat_size) { |
1130 |
s.size = getSize(); |
1131 |
} |
1132 |
} |
1133 |
|
1134 |
uint CroppedFile::read(void *buf, uint size) |
1135 |
{ |
1136 |
FileOfs offset = FileLayer::tell(); |
1137 |
if (offset<mCropStart) return 0; |
1138 |
if (mHasCropSize) { |
1139 |
if (offset >= mCropStart+mCropSize) return 0; |
1140 |
if (offset+size >= mCropStart+mCropSize) size = mCropStart+mCropSize-offset; |
1141 |
} |
1142 |
// ht_printf("CroppedFile::read 0x%08x bytes @ 0x%08qx\n", size, offset); |
1143 |
return FileLayer::read(buf, size); |
1144 |
} |
1145 |
|
1146 |
void CroppedFile::seek(FileOfs offset) |
1147 |
{ |
1148 |
/* if (mHasCropSize) { |
1149 |
... |
1150 |
if (offset>mCropStart) throw IOException(EIO); |
1151 |
}*/ |
1152 |
FileLayer::seek(offset+mCropStart); |
1153 |
} |
1154 |
|
1155 |
FileOfs CroppedFile::tell() const |
1156 |
{ |
1157 |
FileOfs offset = FileLayer::tell(); |
1158 |
if (offset<mCropStart) throw IOException(EIO); |
1159 |
return offset - mCropStart; |
1160 |
} |
1161 |
|
1162 |
void CroppedFile::truncate(FileOfs newsize) |
1163 |
{ |
1164 |
// not implemented because not considered safe |
1165 |
throw IOException(ENOSYS); |
1166 |
} |
1167 |
|
1168 |
uint CroppedFile::write(const void *buf, uint size) |
1169 |
{ |
1170 |
FileOfs offset = FileLayer::tell(); |
1171 |
if (offset<mCropStart) return 0; |
1172 |
if (mHasCropSize) { |
1173 |
if (offset >= mCropStart+mCropSize) return 0; |
1174 |
if (offset+size >= mCropStart+mCropSize) size = mCropStart+mCropSize-offset; |
1175 |
} |
1176 |
return FileLayer::write(buf, size); |
1177 |
} |
1178 |
|
1179 |
/* |
1180 |
* NullFile |
1181 |
*/ |
1182 |
NullFile::NullFile() : File() |
1183 |
{ |
1184 |
} |
1185 |
|
1186 |
void NullFile::extend(FileOfs newsize) |
1187 |
{ |
1188 |
if (newsize!=0) throw IOException(EINVAL); |
1189 |
} |
1190 |
|
1191 |
String &NullFile::getDesc(String &result) const |
1192 |
{ |
1193 |
result = "null device"; |
1194 |
return result; |
1195 |
} |
1196 |
|
1197 |
FileOfs NullFile::getSize() const |
1198 |
{ |
1199 |
return 0; |
1200 |
} |
1201 |
|
1202 |
void NullFile::pstat(pstat_t &s) const |
1203 |
{ |
1204 |
s.caps = pstat_size; |
1205 |
s.size = getSize(); |
1206 |
} |
1207 |
|
1208 |
uint NullFile::read(void *buf, uint size) |
1209 |
{ |
1210 |
return 0; |
1211 |
} |
1212 |
|
1213 |
void NullFile::seek(FileOfs offset) |
1214 |
{ |
1215 |
if (offset!=0) throw IOException(EINVAL); |
1216 |
} |
1217 |
|
1218 |
int NullFile::setAccessMode(IOAccessMode am) |
1219 |
{ |
1220 |
return (am == getAccessMode()) ? 0 : EACCES; |
1221 |
} |
1222 |
|
1223 |
FileOfs NullFile::tell() const |
1224 |
{ |
1225 |
return 0; |
1226 |
} |
1227 |
|
1228 |
void NullFile::truncate(FileOfs newsize) |
1229 |
{ |
1230 |
if (newsize!=0) throw IOException(EINVAL); |
1231 |
} |
1232 |
|
1233 |
uint NullFile::write(const void *buf, uint size) |
1234 |
{ |
1235 |
return 0; |
1236 |
} |
1237 |
|
1238 |
/* |
1239 |
* MemoryFile |
1240 |
*/ |
1241 |
|
1242 |
#define MEMORYFILE_GROW_FACTOR_NUM 4 |
1243 |
#define MEMORYFILE_GROW_FACTOR_DENOM 3 |
1244 |
#define MEMORYFILE_MIN_BUFSIZE 32 |
1245 |
|
1246 |
MemoryFile::MemoryFile(FileOfs o, uint size, IOAccessMode mode) : File() |
1247 |
{ |
1248 |
ofs = o; |
1249 |
dsize = size; |
1250 |
buf = NULL; |
1251 |
ibufsize = size; |
1252 |
if (ibufsize < MEMORYFILE_MIN_BUFSIZE) ibufsize = MEMORYFILE_MIN_BUFSIZE; |
1253 |
resizeBuf(ibufsize); |
1254 |
memset(buf, 0, dsize); |
1255 |
mcount = 0; |
1256 |
|
1257 |
pos = 0; |
1258 |
int e = setAccessMode(mode); |
1259 |
if (e) throw IOException(e); |
1260 |
} |
1261 |
|
1262 |
MemoryFile::~MemoryFile() |
1263 |
{ |
1264 |
free(buf); |
1265 |
} |
1266 |
|
1267 |
byte *MemoryFile::getBufPtr() const |
1268 |
{ |
1269 |
return buf; |
1270 |
} |
1271 |
|
1272 |
void MemoryFile::extend(FileOfs newsize) |
1273 |
{ |
1274 |
// MemoryFiles may not be > 2G |
1275 |
if (newsize > 0x7fffffff) throw IOException(EINVAL); |
1276 |
if (newsize < getSize()) throw IOException(EINVAL); |
1277 |
if (newsize == getSize()) return; |
1278 |
while (bufsize<newsize) extendBuf(); |
1279 |
memset(buf+dsize, 0, newsize-dsize); |
1280 |
dsize = newsize; |
1281 |
mcount++; |
1282 |
} |
1283 |
|
1284 |
void MemoryFile::extendBuf() |
1285 |
{ |
1286 |
resizeBuf(extendBufSize(bufsize)); |
1287 |
} |
1288 |
|
1289 |
uint MemoryFile::extendBufSize(uint bufsize) |
1290 |
{ |
1291 |
return bufsize * MEMORYFILE_GROW_FACTOR_NUM / MEMORYFILE_GROW_FACTOR_DENOM; |
1292 |
} |
1293 |
|
1294 |
IOAccessMode MemoryFile::getAccessMode() const |
1295 |
{ |
1296 |
return Stream::getAccessMode(); |
1297 |
} |
1298 |
|
1299 |
String &MemoryFile::getDesc(String &result) const |
1300 |
{ |
1301 |
result = "MemoryFile"; |
1302 |
return result; |
1303 |
} |
1304 |
|
1305 |
FileOfs MemoryFile::getSize() const |
1306 |
{ |
1307 |
return dsize; |
1308 |
} |
1309 |
|
1310 |
void MemoryFile::pstat(pstat_t &s) const |
1311 |
{ |
1312 |
s.caps = pstat_size; |
1313 |
s.size = getSize(); |
1314 |
} |
1315 |
|
1316 |
uint MemoryFile::read(void *b, uint size) |
1317 |
{ |
1318 |
if ((pos+size) > dsize) { |
1319 |
if (pos >= dsize) return 0; |
1320 |
size = dsize-pos; |
1321 |
} |
1322 |
memcpy(b, buf+pos, size); |
1323 |
pos += size; |
1324 |
return size; |
1325 |
} |
1326 |
|
1327 |
void MemoryFile::resizeBuf(uint newsize) |
1328 |
{ |
1329 |
bufsize = newsize; |
1330 |
|
1331 |
ASSERT(dsize <= bufsize); |
1332 |
|
1333 |
buf = (byte*)realloc(buf, bufsize ? bufsize : 1); |
1334 |
if (!buf) throw std::bad_alloc(); |
1335 |
} |
1336 |
|
1337 |
void MemoryFile::seek(FileOfs o) |
1338 |
{ |
1339 |
if (o<ofs) throw IOException(EINVAL); |
1340 |
pos = o-ofs; |
1341 |
} |
1342 |
|
1343 |
int MemoryFile::setAccessMode(IOAccessMode mode) |
1344 |
{ |
1345 |
int e = Stream::setAccessMode(mode); |
1346 |
if (e) return e; |
1347 |
seek(ofs); |
1348 |
return 0; |
1349 |
} |
1350 |
|
1351 |
uint MemoryFile::shrinkBufSize(uint bufsize) |
1352 |
{ |
1353 |
return bufsize * MEMORYFILE_GROW_FACTOR_DENOM / MEMORYFILE_GROW_FACTOR_NUM; |
1354 |
} |
1355 |
|
1356 |
void MemoryFile::shrinkBuf() |
1357 |
{ |
1358 |
resizeBuf(shrinkBufSize(bufsize)); |
1359 |
} |
1360 |
|
1361 |
FileOfs MemoryFile::tell() const |
1362 |
{ |
1363 |
return pos+ofs; |
1364 |
} |
1365 |
|
1366 |
void MemoryFile::truncate(FileOfs newsize) |
1367 |
{ |
1368 |
dsize = newsize; |
1369 |
|
1370 |
uint s = ibufsize; |
1371 |
while (s<dsize) s = extendBufSize(s); |
1372 |
|
1373 |
resizeBuf(s); |
1374 |
mcount++; |
1375 |
} |
1376 |
|
1377 |
uint MemoryFile::write(const void *b, uint size) |
1378 |
{ |
1379 |
while (pos+size >= bufsize) extendBuf(); |
1380 |
memmove(((byte*)buf)+pos, b, size); |
1381 |
pos += size; |
1382 |
if (pos>dsize) dsize = pos; |
1383 |
mcount++; |
1384 |
return size; |
1385 |
} |
1386 |
|
1387 |
/* |
1388 |
* string stream functions |
1389 |
*/ |
1390 |
|
1391 |
char *fgetstrz(File *file) |
1392 |
{ |
1393 |
FileOfs o = file->tell(); |
1394 |
/* get string size */ |
1395 |
char buf[64]; |
1396 |
int s, z = 0; |
1397 |
bool found = false; |
1398 |
while (!found) { |
1399 |
s = file->read(buf, 64); |
1400 |
for (int i=0; i<s; i++) { |
1401 |
z++; |
1402 |
if (buf[i] == 0) { |
1403 |
found = true; |
1404 |
break; |
1405 |
} |
1406 |
} |
1407 |
} |
1408 |
/* read string */ |
1409 |
char *str = (char*)malloc(z); |
1410 |
if (!str) throw std::bad_alloc(); |
1411 |
file->seek(o); |
1412 |
file->readx(str, z); |
1413 |
return str; |
1414 |
} |
1415 |
|
1416 |
// FIXME: more dynamical solution appreciated |
1417 |
#define REASONABLE_STRING_LIMIT 1024 |
1418 |
|
1419 |
char *getstrz(Stream *stream) |
1420 |
{ |
1421 |
/* get string size */ |
1422 |
char buf[REASONABLE_STRING_LIMIT]; |
1423 |
int z = 0; |
1424 |
while (1) { |
1425 |
stream->readx(buf+z, 1); |
1426 |
z++; |
1427 |
if (z >= REASONABLE_STRING_LIMIT) { |
1428 |
z = REASONABLE_STRING_LIMIT; |
1429 |
break; |
1430 |
} |
1431 |
if (buf[z-1] == 0) break; |
1432 |
} |
1433 |
if (!z) return NULL; |
1434 |
char *str = (char*)malloc(z); |
1435 |
if (!str) throw std::bad_alloc(); |
1436 |
memmove(str, buf, z-1); |
1437 |
str[z-1] = 0; |
1438 |
return str; |
1439 |
} |
1440 |
|
1441 |
void putstrz(Stream *stream, const char *str) |
1442 |
{ |
1443 |
stream->writex(str, strlen(str)+1); |
1444 |
} |
1445 |
|
1446 |
char *getstrp(Stream *stream) |
1447 |
{ |
1448 |
unsigned char l; |
1449 |
stream->readx(&l, 1); |
1450 |
char *str = (char*)malloc(l+1); |
1451 |
if (!str) throw std::bad_alloc(); |
1452 |
stream->readx(str, l); |
1453 |
*(str+l) = 0; |
1454 |
return str; |
1455 |
} |
1456 |
|
1457 |
void putstrp(Stream *stream, const char *str) |
1458 |
{ |
1459 |
unsigned char l = strlen(str); |
1460 |
stream->writex(&l, 1); |
1461 |
stream->writex(str, l); |
1462 |
} |
1463 |
|
1464 |
char *getstrw(Stream *stream) |
1465 |
{ |
1466 |
short t; |
1467 |
byte lbuf[2]; |
1468 |
stream->readx(lbuf, 2); |
1469 |
int l = lbuf[0] | lbuf[1] << 8; |
1470 |
char *a = (char*)malloc(l+1); |
1471 |
if (!a) throw std::bad_alloc(); |
1472 |
for (int i=0; i<l; i++) { |
1473 |
stream->readx(&t, 2); |
1474 |
a[i] = (char)t; |
1475 |
} |
1476 |
a[l] = 0; |
1477 |
return a; |
1478 |
} |
1479 |
|
1480 |
void putstrw(Stream *stream, const char *str) |
1481 |
{ |
1482 |
/* FIXME: someone implement me ? */ |
1483 |
throw NotImplementedException(HERE); |
1484 |
} |
1485 |
|