Files
simgear/simgear/nasal/iolib.c
T
andy b05e32fa8c Sync with Nasal CVS (soon to become Nasal 1.1). Notable new features:
Nasal now supports calls to "subcontexts" and errors can be thrown
across them, leading to complete stack traces when call() is used,
instead of the truncated ones we now see.

Vectors can now be concatenated using the ~ operator that used to work
only for strings.

Better runtime error messages in general due to a fancier
naRuntimeError() implementation

A big data size shrink on 64 bit systems; the size of a naRef dropped
by a factor of two.

"Braceless code blocks" have been added to the parser, so you can
write expressions like "if(a) b();" just like in C.  Note that there's
still a parser bug in there that fails when you nest a braced block
within a braceless one.

Character constants that appear in Nasal source code can now be
literal multibyte UTF8 characters (this was always supported for
string literals, but character constants were forced to be a single
byte).

New modules: "bits", "thread", "utf8" and (gulp...) "io".  The bits
library might be useful to FlightGear, the utf8 one probably not as
Plib does not support wide character text rendering.  The thread
library will work fine for spawning threads to do Nasal stuff, but
obviously contact with the rest of FlightGear must be
hand-synchronized as FlightGear isn't threadsafe.  The io library is
no doubt the most useful, as it exposes all the basic stdio.h
facilities; it's also frighteningly dangerous when combined with
networked code...
2007-03-29 18:50:12 +00:00

227 lines
6.7 KiB
C

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "data.h"
#include "iolib.h"
static void ghostDestroy(void* g);
naGhostType naIOGhostType = { ghostDestroy };
static struct naIOGhost* ioghost(naRef r)
{
if(naGhost_type(r) == &naIOGhostType && IOGHOST(r)->handle)
return naGhost_ptr(r);
return 0;
}
static naRef f_close(naContext c, naRef me, int argc, naRef* args)
{
struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
if(!g) naRuntimeError(c, "bad argument to close()");
if(g->handle) g->type->close(c, g->handle);
g->handle = 0;
return naNil();
}
static naRef f_read(naContext c, naRef me, int argc, naRef* args)
{
struct naIOGhost* g = argc > 0 ? ioghost(args[0]) : 0;
naRef str = argc > 1 ? args[1] : naNil();
naRef len = argc > 2 ? naNumValue(args[2]) : naNil();
if(!g || !MUTABLE(str) || !IS_NUM(len))
naRuntimeError(c, "bad argument to read()");
if(PTR(str).str->len < (int)len.num)
naRuntimeError(c, "string not big enough for read");
return naNum(g->type->read(c, g->handle, (char*)PTR(str).str->data,
(int)len.num));
}
static naRef f_write(naContext c, naRef me, int argc, naRef* args)
{
struct naIOGhost* g = argc > 0 ? ioghost(args[0]) : 0;
naRef str = argc > 1 ? args[1] : naNil();
if(!g || !IS_STR(str))
naRuntimeError(c, "bad argument to write()");
return naNum(g->type->write(c, g->handle, (char*)PTR(str).str->data,
PTR(str).str->len));
}
static naRef f_seek(naContext c, naRef me, int argc, naRef* args)
{
struct naIOGhost* g = argc > 0 ? ioghost(args[0]) : 0;
naRef pos = argc > 1 ? naNumValue(args[1]) : naNil();
naRef whn = argc > 2 ? naNumValue(args[2]) : naNil();
if(!g || !IS_NUM(pos) || !IS_NUM(whn))
naRuntimeError(c, "bad argument to seek()");
g->type->seek(c, g->handle, (int)pos.num, (int)whn.num);
return naNil();
}
static naRef f_tell(naContext c, naRef me, int argc, naRef* args)
{
struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
if(!g)
naRuntimeError(c, "bad argument to tell()");
return naNum(g->type->tell(c, g->handle));
}
static void ghostDestroy(void* g)
{
struct naIOGhost* io = (struct naIOGhost*)g;
io->type->destroy(io->handle);
naFree(io);
}
////////////////////////////////////////////////////////////////////////
// stdio library implementation below:
static void ioclose(naContext c, void* f)
{
if(f)
if(fclose(f) != 0 && c) naRuntimeError(c, strerror(errno));
}
static int ioread(naContext c, void* f, char* buf, unsigned int len)
{
int n;
naModUnlock(); n = fread(buf, 1, len, f); naModLock();
if(n < len && !feof((FILE*)f)) naRuntimeError(c, strerror(errno));
return n;
}
static int iowrite(naContext c, void* f, char* buf, unsigned int len)
{
int n;
naModUnlock(); n = fwrite(buf, 1, len, f); naModLock();
if(ferror((FILE*)f)) naRuntimeError(c, strerror(errno));
return n;
}
static void ioseek(naContext c, void* f, unsigned int off, int whence)
{
if(fseek(f, off, whence) != 0) naRuntimeError(c, strerror(errno));
}
static int iotell(naContext c, void* f)
{
int n = ftell(f);
if(n < 0) naRuntimeError(c, strerror(errno));
return n;
}
static void iodestroy(void* f)
{
if(f != stdin && f != stdout && f != stderr)
ioclose(0, f);
}
struct naIOType naStdIOType = { ioclose, ioread, iowrite, ioseek,
iotell, iodestroy };
naRef naIOGhost(naContext c, FILE* f)
{
struct naIOGhost* ghost = naAlloc(sizeof(struct naIOGhost));
ghost->type = &naStdIOType;
ghost->handle = f;
return naNewGhost(c, &naIOGhostType, ghost);
}
static naRef f_open(naContext c, naRef me, int argc, naRef* args)
{
FILE* f;
naRef file = argc > 0 ? naStringValue(c, args[0]) : naNil();
naRef mode = argc > 1 ? naStringValue(c, args[1]) : naNil();
if(!IS_STR(file)) naRuntimeError(c, "bad argument to open()");
f = fopen((char*)PTR(file).str->data,
IS_STR(mode) ? (const char*)PTR(mode).str->data : "rb");
if(!f) naRuntimeError(c, strerror(errno));
return naIOGhost(c, f);
}
// frees buffer before tossing an error
static int getcguard(naContext ctx, FILE* f, void* buf)
{
char c;
naModUnlock(); c = fgetc(f); naModLock();
if(ferror(f)) {
naFree(buf);
naRuntimeError(ctx, strerror(errno));
}
return c;
}
// Handles multiple EOL conventions by using stdio's ungetc. Will not
// work for other IO types without converting them to FILE* with
// fdopen() or whatnot...
static naRef f_readln(naContext ctx, naRef me, int argc, naRef* args)
{
naRef result;
struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
int i=0, sz = 128;
char c, *buf;
if(!g || g->type != &naStdIOType)
naRuntimeError(ctx, "bad argument to readln()");
buf = naAlloc(sz);
while(1) {
c = getcguard(ctx, g->handle, buf);
if(c == EOF || c == '\n') break;
if(c == '\r') {
char c2 = getcguard(ctx, g->handle, buf);
if(c2 != EOF && c2 != '\n')
if(EOF == ungetc(c2, g->handle))
break;
break;
}
buf[i++] = c;
if(i >= sz) buf = naRealloc(buf, sz *= 2);
}
result = c == EOF ? naNil() : naStr_fromdata(naNewString(ctx), buf, i);
naFree(buf);
return result;
}
static naRef f_stat(naContext ctx, naRef me, int argc, naRef* args)
{
int n=0;
struct stat s;
naRef result, path = argc > 0 ? naStringValue(ctx, args[0]) : naNil();
if(!IS_STR(path)) naRuntimeError(ctx, "bad argument to stat()");
if(stat((char*)PTR(path).str->data, &s) < 0) {
if(errno == ENOENT) return naNil();
naRuntimeError(ctx, strerror(errno));
}
result = naNewVector(ctx);
naVec_setsize(result, 11);
#define FLD(x) naVec_set(result, n++, naNum(s.st_##x));
FLD(dev); FLD(ino); FLD(mode); FLD(nlink); FLD(uid); FLD(gid);
FLD(rdev); FLD(size); FLD(atime); FLD(mtime); FLD(ctime);
#undef FLD
return result;
}
static naCFuncItem funcs[] = {
{ "close", f_close },
{ "read", f_read },
{ "write", f_write },
{ "seek", f_seek },
{ "tell", f_tell },
{ "open", f_open },
{ "readln", f_readln },
{ "stat", f_stat },
{ 0 }
};
naRef naInit_io(naContext c)
{
naRef ns = naGenLib(c, funcs);
naAddSym(c, ns, "SEEK_SET", naNum(SEEK_SET));
naAddSym(c, ns, "SEEK_CUR", naNum(SEEK_CUR));
naAddSym(c, ns, "SEEK_END", naNum(SEEK_END));
naAddSym(c, ns, "stdin", naIOGhost(c, stdin));
naAddSym(c, ns, "stdout", naIOGhost(c, stdout));
naAddSym(c, ns, "stderr", naIOGhost(c, stderr));
return ns;
}