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