]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/iolib.c
9d65fa1e3cab6e080738d21ea28edcdfdd3a2516
[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(fclose(f) != 0 && c) naRuntimeError(c, strerror(errno));
83 }
84
85 static int ioread(naContext c, void* f, char* buf, unsigned int len)
86 {
87     int n;
88     naModUnlock(); n = fread(buf, 1, len, f); naModLock();
89     if(n < len && !feof((FILE*)f)) naRuntimeError(c, strerror(errno));
90     return n;
91 }
92
93 static int iowrite(naContext c, void* f, char* buf, unsigned int len)
94 {
95     int n;
96     naModUnlock(); n = fwrite(buf, 1, len, f); naModLock();
97     if(ferror((FILE*)f)) naRuntimeError(c, strerror(errno));
98     return n;
99 }
100
101 static void ioseek(naContext c, void* f, unsigned int off, int whence)
102 {
103     if(fseek(f, off, whence) != 0) naRuntimeError(c, strerror(errno));
104 }
105
106 static int iotell(naContext c, void* f)
107 {
108     int n = ftell(f);
109     if(n < 0) naRuntimeError(c, strerror(errno));
110     return n;
111 }
112
113 static void iodestroy(void* f)
114 {
115     ioclose(0, f);
116 }
117
118 struct naIOType naStdIOType = { ioclose, ioread, iowrite, ioseek,
119                                 iotell, iodestroy };
120
121 naRef naIOGhost(naContext c, FILE* f)
122 {
123     struct naIOGhost* ghost = naAlloc(sizeof(struct naIOGhost));
124     ghost->type = &naStdIOType;
125     ghost->handle = f;
126     return naNewGhost(c, &naIOGhostType, ghost);
127 }
128
129 static naRef f_open(naContext c, naRef me, int argc, naRef* args)
130 {
131     FILE* f;
132     naRef file = argc > 0 ? naStringValue(c, args[0]) : naNil();
133     naRef mode = argc > 1 ? naStringValue(c, args[1]) : naNil();
134     if(!IS_STR(file)) naRuntimeError(c, "bad argument to open()");
135     f = fopen((char*)file.ref.ptr.str->data,
136               IS_STR(mode) ? (const char*)mode.ref.ptr.str->data : "r");
137     if(!f) naRuntimeError(c, strerror(errno));
138     return naIOGhost(c, f);
139 }
140
141 // frees buffer before tossing an error
142 static int getcguard(naContext ctx, FILE* f, void* buf)
143 {
144     char c;
145     naModUnlock(); c = fgetc(f); naModLock();
146     if(ferror(f)) {
147         naFree(buf);
148         naRuntimeError(ctx, strerror(errno));
149     }
150     return c;
151 }
152
153 // Handles multiple EOL conventions by using stdio's ungetc.  Will not
154 // work for other IO types without converting them to FILE* with
155 // fdopen() or whatnot...
156 static naRef f_readln(naContext ctx, naRef me, int argc, naRef* args)
157 {
158     naRef result;
159     struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
160     int i=0, sz = 128;
161     char* buf;
162     if(!g || g->type != &naStdIOType)
163         naRuntimeError(ctx, "bad argument to readln()");
164     buf = naAlloc(sz);
165     while(1) {
166         char c = getcguard(ctx, g->handle, buf);
167         if(c == EOF || c == '\n') break;
168         if(c == '\r') {
169             char c2 = getcguard(ctx, g->handle, buf);
170             if(c2 != EOF && c2 != '\n')
171                 ungetc(c2, g->handle);
172             break;
173         }
174         buf[i++] = c;
175         if(i >= sz) buf = naRealloc(buf, sz *= 2);
176     }
177     result = naStr_fromdata(naNewString(ctx), buf, i);
178     naFree(buf);
179     return result;
180 }
181
182 static naRef f_stat(naContext ctx, naRef me, int argc, naRef* args)
183 {
184     int n=0;
185     struct stat s;
186     naRef result, path = argc > 0 ? naStringValue(ctx, args[0]) : naNil();
187     if(!IS_STR(path)) naRuntimeError(ctx, "bad argument to stat()");
188     if(stat((char*)path.ref.ptr.str->data, &s) < 0) {
189         if(errno == ENOENT) return naNil();
190         naRuntimeError(ctx, strerror(errno));
191     }
192     result = naNewVector(ctx);
193     naVec_setsize(result, 11);
194 #define FLD(x) naVec_set(result, n++, naNum(s.st_##x));
195     FLD(dev);  FLD(ino);  FLD(mode);  FLD(nlink);  FLD(uid);  FLD(gid);
196     FLD(rdev); FLD(size); FLD(atime); FLD(mtime);  FLD(ctime);
197 #undef FLD
198     return result;
199 }
200
201 static struct func { char* name; naCFunction func; } funcs[] = {
202     { "close", f_close },
203     { "read", f_read },
204     { "write", f_write },
205     { "seek", f_seek },
206     { "tell", f_tell },
207     { "open", f_open },
208     { "readln", f_readln },
209     { "stat", f_stat },
210 };
211
212 static void setsym(naContext c, naRef hash, char* sym, naRef val)
213 {
214     naRef name = naStr_fromdata(naNewString(c), sym, strlen(sym));
215     naHash_set(hash, naInternSymbol(name), val);
216 }
217
218 naRef naIOLib(naContext c)
219 {
220     naRef ns = naNewHash(c);
221     int i, n = sizeof(funcs)/sizeof(struct func);
222     for(i=0; i<n; i++)
223         setsym(c, ns, funcs[i].name,
224                naNewFunc(c, naNewCCode(c, funcs[i].func)));
225     setsym(c, ns, "SEEK_SET", naNum(SEEK_SET));
226     setsym(c, ns, "SEEK_CUR", naNum(SEEK_CUR));
227     setsym(c, ns, "SEEK_END", naNum(SEEK_END));
228     setsym(c, ns, "stdin", naIOGhost(c, stdin));
229     setsym(c, ns, "stdout", naIOGhost(c, stdout));
230     setsym(c, ns, "stderr", naIOGhost(c, stderr));
231     return ns;
232 }