/* Do not remove the headers from this file! see /USAGE for more info. */
#define BUFFER_SIZE 8192
class file_info {
string fname;
string mode;
int pos;
int buffered;
int bpos;
int bchanged;
string buf;
}
mapping open_files = ([]);
class file_info cur_file;
private class file_info find_file(string fn) {
if (fn) {
cur_file = open_files[fn];
if (!cur_file)
error("No such open file '" + fn + "'.\n");
}
return cur_file;
}
private void flush_buffer(class file_info fi) {
if (fi->bchanged) {
write_bytes(fi->fname, fi->pos, fi->buf);
fi->bchanged = 0;
}
}
private void refill_buffer(class file_info fi) {
flush_buffer(fi);
fi->buf = read_bytes(fi->fname, fi->pos, BUFFER_SIZE);
fi->bpos = 0;
fi->buffered = sizeof(fi->buf);
}
void file_open(string fn, string mode) {
int fs = file_size(fn);
int p;
if (fs == -2)
error("file_open(): Is a directory.\n");
switch (mode) {
case "r":
if (fs == -1)
error("file_open(): No such file.\n");
break;
case "w":
case "rw":
break;
case "a":
p = fs;
}
open_files[fn] = cur_file = new(class file_info,
fname : fn,
pos : p);
}
string file_read(string fn, int num) {
class file_info fi = find_file(fn);
string ret;
if (fi->mode[0] != 'r')
error("file_read(): File not open for reading.\n");
if (num <= fi->buffered) {
ret = fi->buf[fi->bpos..fi->bpos + num - 1];
fi->bpos += num;
fi->pos += num;
fi->buffered -= num;
return ret;
} else {
ret = fi->buf[fi->bpos..];
fi->pos += fi->buffered;
num -= fi->buffered;
while (num > BUFFER_SIZE) {
refill_buffer(fi);
ret += fi->buf;
fi->pos += fi->buffered;
num -= fi->buffered;
if (fi->buffered != BUFFER_SIZE)
break;
}
refill_buffer(fi);
if (num > fi->buffered) {
ret += fi->buf;
fi->pos += fi->buffered;
fi->buffered = 0;
fi->bpos += fi->buffered;
} else {
ret += fi->buf[0..num-1];
fi->pos += num;
fi->bpos += num;
fi->buffered -= num;
}
return ret;
}
}
string file_readline(string fn) {
class file_info fi = find_file(fn);
string ret;
int where;
if (fi->mode[0] != 'r')
error("file_read(): File not open for reading.\n");
where = member_array('\n', fi->buf, fi->bpos);
if (where != -1) {
ret = fi->buf[fi->bpos..where - 1];
fi->bpos = where + 1;
fi->pos += (where + 1 - fi->bpos);
fi->buffered -= (where + 1 - fi->bpos);
return ret;
} else {
ret = fi->buf[fi->bpos..];
fi->pos += fi->buffered;
while (1) {
refill_buffer(fi);
where = member_array('\n', fi->buf, fi->bpos);
if (where != -1) {
ret = fi->buf[fi->bpos..where - 1];
fi->bpos = where + 1;
fi->pos += (where + 1 - fi->bpos);
fi->buffered -= (where + 1 - fi->bpos);
return ret;
}
ret += fi->buf;
fi->pos += fi->buffered;
if (fi->buffered != BUFFER_SIZE)
return ret;
}
}
}
void file_seek(string fn, int dist, string how) {
class file_info fi = find_file(fn);
int rel;
if (fi->mode == "a")
error("file_seek(): Cannot seek on a file opened for append.\n");
switch (how) {
case "start":
rel = dist - fi->pos;
break;
case "cur":
rel = dist;
break;
case "end":
rel = file_size(fi->fname) + dist - fi->pos;
}
if (rel + fi->pos < 0)
rel = -fi->pos;
fi->pos += rel;
if (rel > fi->buffered || (-rel) > fi->bpos) {
refill_buffer(fi);
} else {
fi->bpos += rel;
fi->buffered -= rel;
}
}
void file_close(string fn) {
class file_info fi = find_file(fn);
flush_buffer(fi);
map_delete(open_files, fn);
}
void file_flush(string fn) {
class file_info fi = find_file(fn);
flush_buffer(fi);
}
int file_pos(string fn) {
class file_info fi = find_file(fn);
return fi->pos;
}