]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/iolib.c
easyxml.cxx: add missing endXML visitor call
[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 && IOGHOST(r)->handle)
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(PTR(str).str->len < (int)len.num)
36         naRuntimeError(c, "string not big enough for read");
37     return naNum(g->type->read(c, g->handle, (char*)PTR(str).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*)PTR(str).str->data,
48                                 PTR(str).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     if(f != stdin && f != stdout && f != stderr)
117         ioclose(0, f);
118 }
119
120 struct naIOType naStdIOType = { ioclose, ioread, iowrite, ioseek,
121                                 iotell, iodestroy };
122
123 naRef naIOGhost(naContext c, FILE* f)
124 {
125     struct naIOGhost* ghost = naAlloc(sizeof(struct naIOGhost));
126     ghost->type = &naStdIOType;
127     ghost->handle = f;
128     return naNewGhost(c, &naIOGhostType, ghost);
129 }
130
131 static naRef f_open(naContext c, naRef me, int argc, naRef* args)
132 {
133     FILE* f;
134     naRef file = argc > 0 ? naStringValue(c, args[0]) : naNil();
135     naRef mode = argc > 1 ? naStringValue(c, args[1]) : naNil();
136     if(!IS_STR(file)) naRuntimeError(c, "bad argument to open()");
137     f = fopen((char*)PTR(file).str->data,
138               IS_STR(mode) ? (const char*)PTR(mode).str->data : "rb");
139     if(!f) naRuntimeError(c, strerror(errno));
140     return naIOGhost(c, f);
141 }
142
143 // frees buffer before tossing an error
144 static int getcguard(naContext ctx, FILE* f, void* buf)
145 {
146     char c;
147     naModUnlock(); c = fgetc(f); naModLock();
148     if(ferror(f)) {
149         naFree(buf);
150         naRuntimeError(ctx, strerror(errno));
151     }
152     return c;
153 }
154
155 // Handles multiple EOL conventions by using stdio's ungetc.  Will not
156 // work for other IO types without converting them to FILE* with
157 // fdopen() or whatnot...
158 static naRef f_readln(naContext ctx, naRef me, int argc, naRef* args)
159 {
160     naRef result;
161     struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
162     int i=0, sz = 128;
163     char c, *buf;
164     if(!g || g->type != &naStdIOType)
165         naRuntimeError(ctx, "bad argument to readln()");
166     buf = naAlloc(sz);
167     while(1) {
168         c = getcguard(ctx, g->handle, buf);
169         if(c == EOF || c == '\n') break;
170         if(c == '\r') {
171             char c2 = getcguard(ctx, g->handle, buf);
172             if(c2 != EOF && c2 != '\n')
173                 if(EOF == ungetc(c2, g->handle))
174                     break;
175             break;
176         }
177         buf[i++] = c;
178         if(i >= sz) buf = naRealloc(buf, sz *= 2);
179     }
180     result = c == EOF ? naNil() : naStr_fromdata(naNewString(ctx), buf, i);
181     naFree(buf);
182     return result;
183 }
184
185 static naRef f_stat(naContext ctx, naRef me, int argc, naRef* args)
186 {
187     int n=0;
188     struct stat s;
189     naRef result, path = argc > 0 ? naStringValue(ctx, args[0]) : naNil();
190     if(!IS_STR(path)) naRuntimeError(ctx, "bad argument to stat()");
191     if(stat((char*)PTR(path).str->data, &s) < 0) {
192         if(errno == ENOENT) return naNil();
193         naRuntimeError(ctx, strerror(errno));
194     }
195     result = naNewVector(ctx);
196     naVec_setsize(result, 11);
197 #define FLD(x) naVec_set(result, n++, naNum(s.st_##x));
198     FLD(dev);  FLD(ino);  FLD(mode);  FLD(nlink);  FLD(uid);  FLD(gid);
199     FLD(rdev); FLD(size); FLD(atime); FLD(mtime);  FLD(ctime);
200 #undef FLD
201     return result;
202 }
203
204 static naCFuncItem funcs[] = {
205     { "close", f_close },
206     { "read", f_read },
207     { "write", f_write },
208     { "seek", f_seek },
209     { "tell", f_tell },
210     { "open", f_open },
211     { "readln", f_readln },
212     { "stat", f_stat },
213     { 0 }
214 };
215
216 naRef naInit_io(naContext c)
217 {
218     naRef ns = naGenLib(c, funcs);
219     naAddSym(c, ns, "SEEK_SET", naNum(SEEK_SET));
220     naAddSym(c, ns, "SEEK_CUR", naNum(SEEK_CUR));
221     naAddSym(c, ns, "SEEK_END", naNum(SEEK_END));
222     naAddSym(c, ns, "stdin", naIOGhost(c, stdin));
223     naAddSym(c, ns, "stdout", naIOGhost(c, stdout));
224     naAddSym(c, ns, "stderr", naIOGhost(c, stderr));
225     return ns;
226 }