]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/iolib.c
Merge branch 'releng/1.9.1' into maint
[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 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(naStr_data(file), IS_STR(mode) ? naStr_data(mode) : "rb");
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     int 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, c, sz = 128;
162     char *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             int c2 = getcguard(ctx, g->handle, buf);
171             if(c2 != EOF && c2 != '\n')
172                 if(EOF == ungetc(c2, g->handle))
173                     break;
174             break;
175         }
176         buf[i++] = c;
177         if(i >= sz) buf = naRealloc(buf, sz *= 2);
178     }
179     result = c == EOF ? naNil() : naStr_fromdata(naNewString(ctx), buf, i);
180     naFree(buf);
181     return result;
182 }
183
184 static naRef f_stat(naContext ctx, naRef me, int argc, naRef* args)
185 {
186     int n=0;
187     struct stat s;
188     naRef result, path = argc > 0 ? naStringValue(ctx, args[0]) : naNil();
189     if(!IS_STR(path)) naRuntimeError(ctx, "bad argument to stat()");
190     if(stat(naStr_data(path), &s) < 0) {
191         if(errno == ENOENT) return naNil();
192         naRuntimeError(ctx, strerror(errno));
193     }
194     result = naNewVector(ctx);
195     naVec_setsize(result, 11);
196 #define FLD(x) naVec_set(result, n++, naNum(s.st_##x));
197     FLD(dev);  FLD(ino);  FLD(mode);  FLD(nlink);  FLD(uid);  FLD(gid);
198     FLD(rdev); FLD(size); FLD(atime); FLD(mtime);  FLD(ctime);
199 #undef FLD
200     return result;
201 }
202
203 static naCFuncItem funcs[] = {
204     { "close", f_close },
205     { "read", f_read },
206     { "write", f_write },
207     { "seek", f_seek },
208     { "tell", f_tell },
209     { "open", f_open },
210     { "readln", f_readln },
211     { "stat", f_stat },
212     { 0 }
213 };
214
215 naRef naInit_io(naContext c)
216 {
217     naRef ns = naGenLib(c, funcs);
218     naAddSym(c, ns, "SEEK_SET", naNum(SEEK_SET));
219     naAddSym(c, ns, "SEEK_CUR", naNum(SEEK_CUR));
220     naAddSym(c, ns, "SEEK_END", naNum(SEEK_END));
221     naAddSym(c, ns, "stdin", naIOGhost(c, stdin));
222     naAddSym(c, ns, "stdout", naIOGhost(c, stdout));
223     naAddSym(c, ns, "stderr", naIOGhost(c, stderr));
224     return ns;
225 }