]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/iolib.c
Removal of PLIB/SG from SimGear
[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, "iofile" };
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(naStr_len(str) < (int)len.num)
36         naRuntimeError(c, "string not big enough for read");
37     return naNum(g->type->read(c, g->handle, naStr_data(str),
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, naStr_data(str),
48                                 naStr_len(str)));
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 naRef f_flush(naContext c, naRef me, int argc, naRef* args)
71 {
72     struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
73     if(!g)
74         naRuntimeError(c, "bad argument to flush()");
75     g->type->flush(c, g->handle);
76     return naNil();
77 }
78
79 static void ghostDestroy(void* g)
80 {
81     struct naIOGhost* io = (struct naIOGhost*)g;
82     io->type->destroy(io->handle);
83     naFree(io);
84 }
85
86 ////////////////////////////////////////////////////////////////////////
87 // stdio library implementation below:
88
89 static void ioclose(naContext c, void* f)
90 {
91     if(f)
92         if(fclose(f) != 0 && c) naRuntimeError(c, strerror(errno));
93 }
94
95 static int ioread(naContext c, void* f, char* buf, unsigned int len)
96 {
97     int n;
98     naModUnlock(); n = fread(buf, 1, len, f); naModLock();
99     if(n < len && !feof((FILE*)f)) naRuntimeError(c, strerror(errno));
100     return n;
101 }
102
103 static int iowrite(naContext c, void* f, char* buf, unsigned int len)
104 {
105     int n;
106     naModUnlock(); n = fwrite(buf, 1, len, f); naModLock();
107     if(ferror((FILE*)f)) naRuntimeError(c, strerror(errno));
108     return n;
109 }
110
111 static void ioseek(naContext c, void* f, unsigned int off, int whence)
112 {
113     if(fseek(f, off, whence) != 0) naRuntimeError(c, strerror(errno));
114 }
115
116 static int iotell(naContext c, void* f)
117 {
118     int n = ftell(f);
119     if(n < 0) naRuntimeError(c, strerror(errno));
120     return n;
121 }
122
123 static void ioflush(naContext c, void* f)
124 {
125     if(fflush(f)) naRuntimeError(c, strerror(errno));
126 }
127
128 static void iodestroy(void* f)
129 {
130     if(f != stdin && f != stdout && f != stderr)
131         ioclose(0, f);
132 }
133
134 struct naIOType naStdIOType = { ioclose, ioread, iowrite, ioseek,
135                                 iotell, ioflush, iodestroy };
136
137 naRef naIOGhost(naContext c, FILE* f)
138 {
139     struct naIOGhost* ghost = naAlloc(sizeof(struct naIOGhost));
140     ghost->type = &naStdIOType;
141     ghost->handle = f;
142     return naNewGhost(c, &naIOGhostType, ghost);
143 }
144
145 static naRef f_open(naContext c, naRef me, int argc, naRef* args)
146 {
147     FILE* f;
148     naRef file = argc > 0 ? naStringValue(c, args[0]) : naNil();
149     naRef mode = argc > 1 ? naStringValue(c, args[1]) : naNil();
150     if(!IS_STR(file)) naRuntimeError(c, "bad argument to open()");
151     f = fopen(naStr_data(file), IS_STR(mode) ? naStr_data(mode) : "rb");
152     if(!f) naRuntimeError(c, strerror(errno));
153     return naIOGhost(c, f);
154 }
155
156 // frees buffer before tossing an error
157 static int getcguard(naContext ctx, FILE* f, void* buf)
158 {
159     int c;
160     naModUnlock(); c = fgetc(f); naModLock();
161     if(ferror(f)) {
162         naFree(buf);
163         naRuntimeError(ctx, strerror(errno));
164     }
165     return c;
166 }
167
168 // Handles multiple EOL conventions by using stdio's ungetc.  Will not
169 // work for other IO types without converting them to FILE* with
170 // fdopen() or whatnot...
171 static naRef f_readln(naContext ctx, naRef me, int argc, naRef* args)
172 {
173     naRef result;
174     struct naIOGhost* g = argc==1 ? ioghost(args[0]) : 0;
175     int i=0, c, sz = 128;
176     char *buf;
177     if(!g || g->type != &naStdIOType)
178         naRuntimeError(ctx, "bad argument to readln()");
179     buf = naAlloc(sz);
180     while(1) {
181         c = getcguard(ctx, g->handle, buf);
182         if(c == EOF || c == '\n') break;
183         if(c == '\r') {
184             int c2 = getcguard(ctx, g->handle, buf);
185             if(c2 != EOF && c2 != '\n')
186                 if(EOF == ungetc(c2, g->handle))
187                     break;
188             break;
189         }
190         buf[i++] = c;
191         if(i >= sz) buf = naRealloc(buf, sz *= 2);
192     }
193     result = c == EOF ? naNil() : naStr_fromdata(naNewString(ctx), buf, i);
194     naFree(buf);
195     return result;
196 }
197
198 #ifdef _WIN32
199 #define S_ISLNK(m) 0
200 #define S_ISSOCK(m) 0
201 #endif
202 #ifdef _MSC_VER
203 #define S_ISREG(m) (((m)&_S_IFMT)==_S_IFREG)
204 #define S_ISDIR(m) (((m)&_S_IFMT)==_S_IFDIR)
205 #define S_ISCHR(m) (((m)&_S_IFMT)==_S_IFCHR)
206 #define S_ISFIFO(m) (((m)&_S_IFMT)==_S_IFIFO)
207 #define S_ISBLK(m) 0
208 typedef unsigned short mode_t;
209 #endif
210 static naRef ftype(naContext ctx, mode_t m)
211 {
212     const char* t = "unk";
213     if(S_ISREG(m))  t = "reg";
214     else if(S_ISDIR(m)) t = "dir"; else if(S_ISCHR(m))  t = "chr";
215     else if(S_ISBLK(m)) t = "blk"; else if(S_ISFIFO(m)) t = "fifo";
216     else if(S_ISLNK(m)) t = "lnk"; else if(S_ISSOCK(m)) t = "sock";
217     return naStr_fromdata(naNewString(ctx), t, strlen(t));
218 }
219
220 static naRef f_stat(naContext ctx, naRef me, int argc, naRef* args)
221 {
222     int n=0;
223     struct stat s;
224     naRef result, path = argc > 0 ? naStringValue(ctx, args[0]) : naNil();
225     if(!IS_STR(path)) naRuntimeError(ctx, "bad argument to stat()");
226     if(stat(naStr_data(path), &s) < 0) {
227         if(errno == ENOENT) return naNil();
228         naRuntimeError(ctx, strerror(errno));
229     }
230     result = naNewVector(ctx);
231     naVec_setsize(result, 12);
232 #define FLD(x) naVec_set(result, n++, naNum(s.st_##x));
233     FLD(dev);  FLD(ino);  FLD(mode);  FLD(nlink);  FLD(uid);  FLD(gid);
234     FLD(rdev); FLD(size); FLD(atime); FLD(mtime);  FLD(ctime);
235 #undef FLD
236     naVec_set(result, n++, ftype(ctx, s.st_mode));
237     return result;
238 }
239
240 static naCFuncItem funcs[] = {
241     { "close", f_close },
242     { "read", f_read },
243     { "write", f_write },
244     { "seek", f_seek },
245     { "tell", f_tell },
246     { "flush", f_flush },
247     { "open", f_open },
248     { "readln", f_readln },
249     { "stat", f_stat },
250     { 0 }
251 };
252
253 naRef naInit_io(naContext c)
254 {
255     naRef ns = naGenLib(c, funcs);
256     naAddSym(c, ns, "SEEK_SET", naNum(SEEK_SET));
257     naAddSym(c, ns, "SEEK_CUR", naNum(SEEK_CUR));
258     naAddSym(c, ns, "SEEK_END", naNum(SEEK_END));
259     naAddSym(c, ns, "stdin", naIOGhost(c, stdin));
260     naAddSym(c, ns, "stdout", naIOGhost(c, stdout));
261     naAddSym(c, ns, "stderr", naIOGhost(c, stderr));
262     return ns;
263 }