]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/iolib.c
b9c81f33a0cc186f2fde0af1b37bf39dc4975d96
[simgear.git] / simgear / nasal / iolib.c
1 #include <errno.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include "data.h"
7 #include "iolib.h"
8
9 static void ghostDestroy(void* g);
10 naGhostType naIOGhostType = { ghostDestroy };
11
12 static struct naIOGhost* ioghost(naRef r)
13 {
14     if(naGhost_type(r) == &naIOGhostType)
15         return naGhost_ptr(r);
16     return 0;
17 }
18
19 static naRef f_close(naContext c, naRef me, int argc, naRef* args)
20 {
21     struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
22     if(!g) naRuntimeError(c, "bad argument to close()");
23     if(g->handle) g->type->close(c, g->handle);
24     g->handle = 0;
25     return naNil();
26 }
27
28 static naRef f_read(naContext c, naRef me, int argc, naRef* args)
29 {
30     struct naIOGhost* g = argc > 0 ? ioghost(args[0]) : 0;
31     naRef str = argc > 1 ? args[1] : naNil();
32     naRef len = argc > 2 ? naNumValue(args[2]) : naNil();
33     if(!g || !MUTABLE(str) || !IS_NUM(len))
34         naRuntimeError(c, "bad argument to read()");
35     if(str.ref.ptr.str->len < (int)len.num)
36         naRuntimeError(c, "string not big enough for read");
37     return naNum(g->type->read(c, g->handle, (char*)str.ref.ptr.str->data,
38                                (int)len.num));
39 }
40
41 static naRef f_write(naContext c, naRef me, int argc, naRef* args)
42 {
43     struct naIOGhost* g = argc > 0 ? ioghost(args[0]) : 0;
44     naRef str = argc > 1 ? args[1] : naNil();
45     if(!g || !IS_STR(str))
46         naRuntimeError(c, "bad argument to write()");
47     return naNum(g->type->write(c, g->handle, (char*)str.ref.ptr.str->data,
48                                 str.ref.ptr.str->len));
49 }
50
51 static naRef f_seek(naContext c, naRef me, int argc, naRef* args)
52 {
53     struct naIOGhost* g = argc > 0 ? ioghost(args[0]) : 0;
54     naRef pos = argc > 1 ? naNumValue(args[1]) : naNil();
55     naRef whn = argc > 2 ? naNumValue(args[2]) : naNil();
56     if(!g || !IS_NUM(pos) || !IS_NUM(whn))
57         naRuntimeError(c, "bad argument to seek()");
58     g->type->seek(c, g->handle, (int)pos.num, (int)whn.num);
59     return naNil();
60 }
61
62 static naRef f_tell(naContext c, naRef me, int argc, naRef* args)
63 {
64     struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
65     if(!g)
66         naRuntimeError(c, "bad argument to tell()");
67     return naNum(g->type->tell(c, g->handle));
68 }
69
70 static void ghostDestroy(void* g)
71 {
72     struct naIOGhost* io = (struct naIOGhost*)g;
73     io->type->destroy(io->handle);
74     naFree(io);
75 }
76
77 ////////////////////////////////////////////////////////////////////////
78 // stdio library implementation below:
79
80 static void ioclose(naContext c, void* f)
81 {
82     if(f)
83         if(fclose(f) != 0 && c) naRuntimeError(c, strerror(errno));
84 }
85
86 static int ioread(naContext c, void* f, char* buf, unsigned int len)
87 {
88     int n;
89     naModUnlock(); n = fread(buf, 1, len, f); naModLock();
90     if(n < len && !feof((FILE*)f)) naRuntimeError(c, strerror(errno));
91     return n;
92 }
93
94 static int iowrite(naContext c, void* f, char* buf, unsigned int len)
95 {
96     int n;
97     naModUnlock(); n = fwrite(buf, 1, len, f); naModLock();
98     if(ferror((FILE*)f)) naRuntimeError(c, strerror(errno));
99     return n;
100 }
101
102 static void ioseek(naContext c, void* f, unsigned int off, int whence)
103 {
104     if(fseek(f, off, whence) != 0) naRuntimeError(c, strerror(errno));
105 }
106
107 static int iotell(naContext c, void* f)
108 {
109     int n = ftell(f);
110     if(n < 0) naRuntimeError(c, strerror(errno));
111     return n;
112 }
113
114 static void iodestroy(void* f)
115 {
116     ioclose(0, f);
117 }
118
119 struct naIOType naStdIOType = { ioclose, ioread, iowrite, ioseek,
120                                 iotell, iodestroy };
121
122 naRef naIOGhost(naContext c, FILE* f)
123 {
124     struct naIOGhost* ghost = naAlloc(sizeof(struct naIOGhost));
125     ghost->type = &naStdIOType;
126     ghost->handle = f;
127     return naNewGhost(c, &naIOGhostType, ghost);
128 }
129
130 static naRef f_open(naContext c, naRef me, int argc, naRef* args)
131 {
132     FILE* f;
133     naRef file = argc > 0 ? naStringValue(c, args[0]) : naNil();
134     naRef mode = argc > 1 ? naStringValue(c, args[1]) : naNil();
135     if(!IS_STR(file)) naRuntimeError(c, "bad argument to open()");
136     f = fopen((char*)file.ref.ptr.str->data,
137               IS_STR(mode) ? (const char*)mode.ref.ptr.str->data : "r");
138     if(!f) naRuntimeError(c, strerror(errno));
139     return naIOGhost(c, f);
140 }
141
142 // frees buffer before tossing an error
143 static int getcguard(naContext ctx, FILE* f, void* buf)
144 {
145     char c;
146     naModUnlock(); c = fgetc(f); naModLock();
147     if(ferror(f)) {
148         naFree(buf);
149         naRuntimeError(ctx, strerror(errno));
150     }
151     return c;
152 }
153
154 // Handles multiple EOL conventions by using stdio's ungetc.  Will not
155 // work for other IO types without converting them to FILE* with
156 // fdopen() or whatnot...
157 static naRef f_readln(naContext ctx, naRef me, int argc, naRef* args)
158 {
159     naRef result;
160     struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
161     int i=0, sz = 128;
162     char c, *buf;
163     if(!g || g->type != &naStdIOType)
164         naRuntimeError(ctx, "bad argument to readln()");
165     buf = naAlloc(sz);
166     while(1) {
167         c = getcguard(ctx, g->handle, buf);
168         if(c == EOF || c == '\n') break;
169         if(c == '\r') {
170             char c2 = getcguard(ctx, g->handle, buf);
171             if(c2 != EOF && c2 != '\n')
172                 ungetc(c2, g->handle);
173             break;
174         }
175         buf[i++] = c;
176         if(i >= sz) buf = naRealloc(buf, sz *= 2);
177     }
178     result = c == EOF ? naNil() : naStr_fromdata(naNewString(ctx), buf, i);
179     naFree(buf);
180     return result;
181 }
182
183 static naRef f_stat(naContext ctx, naRef me, int argc, naRef* args)
184 {
185     int n=0;
186     struct stat s;
187     naRef result, path = argc > 0 ? naStringValue(ctx, args[0]) : naNil();
188     if(!IS_STR(path)) naRuntimeError(ctx, "bad argument to stat()");
189     if(stat((char*)path.ref.ptr.str->data, &s) < 0) {
190         if(errno == ENOENT) return naNil();
191         naRuntimeError(ctx, strerror(errno));
192     }
193     result = naNewVector(ctx);
194     naVec_setsize(result, 11);
195 #define FLD(x) naVec_set(result, n++, naNum(s.st_##x));
196     FLD(dev);  FLD(ino);  FLD(mode);  FLD(nlink);  FLD(uid);  FLD(gid);
197     FLD(rdev); FLD(size); FLD(atime); FLD(mtime);  FLD(ctime);
198 #undef FLD
199     return result;
200 }
201
202 static struct func { char* name; naCFunction func; } funcs[] = {
203     { "close", f_close },
204     { "read", f_read },
205     { "write", f_write },
206     { "seek", f_seek },
207     { "tell", f_tell },
208     { "open", f_open },
209     { "readln", f_readln },
210     { "stat", f_stat },
211 };
212
213 static void setsym(naContext c, naRef hash, char* sym, naRef val)
214 {
215     naRef name = naStr_fromdata(naNewString(c), sym, strlen(sym));
216     naHash_set(hash, naInternSymbol(name), val);
217 }
218
219 naRef naIOLib(naContext c)
220 {
221     naRef ns = naNewHash(c);
222     int i, n = sizeof(funcs)/sizeof(struct func);
223     for(i=0; i<n; i++)
224         setsym(c, ns, funcs[i].name,
225                naNewFunc(c, naNewCCode(c, funcs[i].func)));
226     setsym(c, ns, "SEEK_SET", naNum(SEEK_SET));
227     setsym(c, ns, "SEEK_CUR", naNum(SEEK_CUR));
228     setsym(c, ns, "SEEK_END", naNum(SEEK_END));
229     setsym(c, ns, "stdin", naIOGhost(c, stdin));
230     setsym(c, ns, "stdout", naIOGhost(c, stdout));
231     setsym(c, ns, "stderr", naIOGhost(c, stderr));
232     return ns;
233 }